通过填充一个元素

时间:2016-04-03 05:30:07

标签: c++ c++11 stdarray

我有一个列表类,其中size变量是const成员。这对我很有帮助,因为它强制要求列表的大小可能因运行而异,但在单个运行中不能有所不同。

我想创建这些列表的集合。集合中的列表数量是模板变量,所以我想使用std::array ...即,我想要一个列表数组,其中数组的大小是模板参数,每个列表的大小是在构造中指定的const

不幸的是:

  • const-size列表没有默认构造函数(需要指定它的大小!),所以我需要为列表的每个元素提供一个构造函数参数。我不能只创建数组然后设置元素
  • 因为我的列表大小是模板变量,所以我不能使用标准的初始化列表 - 所需的数字元素各不相同

我认识到有其他选择:

  • 我可以逐个使用std::vectorpush_back个元素,直到向量的大小等于我的模板参数,但这似乎不优雅,因为它不会自然地强制执行条件在完全填充后,不应更改生成的矢量大小。
  • 我可以翻转索引排序,并且有一个常量大小的std::arrays列表。但是,这并不适合我的其余代码;我希望能够将数组中的单个const大小的列表传递给客户端代码
  • 我可以为const大小的列表类创建一个默认构造函数,创建数组,然后使用placement new逐个替换数组元素。这似乎可能有一些不好的副作用(const大小的列表的默认构造函数是做什么的?如果在其他地方意外地调用它会怎么样?当我的继任者不知道我做了什么时会发生什么?)

由于这些都不是完全理想的,我认为如果有一个数组构造函数(或辅助函数)可以作为参数:

  1. T
  2. 数组中的元素数
  3. 单个T对象
  4. ...并返回std::array<T>,其中每个T都是从参数2复制构造的。

    这样的事情存在吗?

2 个答案:

答案 0 :(得分:2)

确定。模板魔术。由于std::array是聚合类型,因此可以使用聚合初始化来初始化它:

std::array<T, 5> arr = { one, two, three, four, five };

这个想法是one,...,five是T类型构造对象的五个副本(在您的情况下为list),使用用户输入作为参数。所以,让我们玩吧。读完之后不要笑或哭:

我们的想法是获取一个T类型的对象,并将其复制五次:

 { T(param), .... }; // Five repetitions.

因此,让我们创建一个返回已经初始化的数组的函数:

std::array<A, 5> arr = create_array_by_copy<5>(A(10));

该函数将返回array<A, 5>5个临时对象的副本。

为此,我们将使用辅助struct,它将创建一个长度为5的参数包(参数化为s):

template<std::size_t s, class... voids_t>
struct sized_pack : public sized_pack<s - 1, voids_t..., void>
{};

template<class... voids_t>
struct sized_pack<0, voids_t...>
{};

这将创建一个名为voids_t的参数包,它只是s void的列表。而现在,诀窍的核心是:

template<std::size_t s, class T, class... pack_t>
std::array<T, s>
create_array_by_copy_helper(sized_pack<0, pack_t...> const&,
                            T const& o)
{ return { (pack_t(), o)... }; }

template<std::size_t s, class T>
std::array<T, s> create_array_by_copy(T const& o)
{
    return create_array_by_copy_helper<s>(sized_pack<s>(), o);
}

这很复杂。我知道......因为我们已经将类型sized_pack<s>的对象传递给辅助函数,该临时对象将实例化sized_pack的层次结构,其最后一个基类将是{{1}类型的对象}。

函数apply将接收该对象作为sized_pack<0, void, void, void, void, void>的引用(最后一个基类,注意前0),因此,size_pack<0, pack_t...>将是我们的5 pack_t个列表

最后,我们有:

void

只是逗号运算符,因此返回 (pack_t(), o) 。我们的想法是我们在“模式”中插入了o(参数包),因此,当将pack_t应用于表达式时,它将被逗号分隔的表达式替换,其中每个... 1}}外观将由参数包中的每个元素以相同的顺序替换,因此:

pack_t

转换为:

  { (pack_t(), o)... }

初始化列表!!最后,每个元素只是一个 { (void(), o), (void(), o), (void(), o), (void(), o), (void(), o) } 表达式,后跟逗号运算符,每个对的第二个元素将由逗号运算符返回。因此,评估的表达式将是:

void

我们想要的初始化列表!!

Coliru例子:

http://coliru.stacked-crooked.com/a/d6c4ab6c9b203130

您只需要将类型 return { o, o, o, o, o }; // With its corresponding calls to `forward`. 替换为列表类。

答案 1 :(得分:0)

std::array<T, N>是一个模板类,表示N类型元素的长度为T的数组。如果要创建列表数组,只需执行std::array<std::list<T>>, N>,其中N编译时已知。 const std::size_t N是不够的,必须是constexpr

所以不,你不能这样做。但是,您可以使用std::vector

如果您发布了一些代码,我们可以提出更好的建议。

相关问题