2016-04-03 61 views
1

我有一个列表类,其中size变量是const成员。这对我很有帮助,因为它强制要求列表的大小在运行中可能会有所不同,但在单个运行中不能改变。通过填充一个元素构造std :: array

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

不幸的是指定的const

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

我认识到,有替代品:

  • 我可以用一个std::vector,只是push_back元素逐一直到矢量的大小等于我的模板参数,但这似乎不雅,因为它不会自然地执行的条件,结果矢量的大小在完全填充后不应该改变。
  • 我可以翻转索引排序,并有一个常规大小的列表std::arrays。然而,这与我的其他代码不太吻合。我希望能够从数组传递一个单独的常量大小的列表到客户端代码
  • 我可以为const大小的列表类创建一个默认构造函数,创建该数组,然后使用placement new替换数组元素一个接一个。这似乎是它可能有一些不良的副作用(这是什么常量大小的列表的默认构造函数吗?如果它叫什么意外别处?当我的继任者已经不知道我做了什么,会发生什么?)

由于这些都不是完全理想,我想,这将是巨大的,如果有数组构造(或辅助功能),它会采取,作为参数:

  1. 元素T的阵列中的数
  2. 单个T对象

...并返回std::array<T>,其中每个T已从参数2复制构建。

这样的事情是否存在?

+0

“每个列表的大小都是在构造时指定的常量” - 我衷心希望你的意思是在*编译时*,对吗?否则,你可以折腾从一开始就使用'std :: array '的想法。 – WhozCraig

+0

列表的大小不是编译时间常量。 – user1476176

回答

2

好的。模板魔术。

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

的想法是one,...,five是T型(在你的案件清单)的构造对象的五个人副本,使用:作为std::array是一个聚合类型,它可以使用集合初始化进行初始化用户输入为参数。所以,让我们玩吧。别笑或者看完这个哭:

的想法是类型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,这仅仅是列表svoid s。而现在,诀窍的核心是:

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的层次结构,最后一个基类将是sized_pack<0, void, void, void, void, void>类型的对象。

功能apply将接收该对象作为参考size_pack<0, pack_t...>(最后一个基类,请注意第一个0),因此,pack_t将是我们的5 void s的列表。

最后,我们有:

(pack_t(), o) 

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

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

转化为:

{ (void(), o), (void(), o), (void(), o), (void(), o), (void(), o) } 

初始化列表!最后,每个元素只是一个void表达式,后跟昏迷操作符,每个对的第二个元素将由逗号运算符返回。因此,评估的表达式将为:

return { o, o, o, o, o }; // With its corresponding calls to `forward`. 

我们希望的初始化列表!!

Coliru例如:

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

你只需要与您的清单类取代型T

+0

为什么重新创建'std :: make_integer_sequence'? –

+0

好的,我不知道它存在。无论如何,它是一个C++ 14特性,今天仍然使用一些编译器版本(如g ++ 4.8.4),并没有完全支持C++ 14。 –

+1

酷!我接受这个答案,但你也可能有兴趣看到别人给我的问题以前的化身答案:http://stackoverflow.com/questions/18497122/how-to-initialize-stdarrayt-n-优雅如果不是默认构造 – user1476176

0

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

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

如果您发布了您的努力的一些代码,我们可以想出更好的东西。

+0

谢谢!我在想,也许最简单的方法是将一个std ::指向数组的指针存储到常量大小的列表中,然后使用new()创建具有适当大小的列表。我不得不担心新的和删除,但它确保了我可以在以后的大小 – user1476176

+0

@ user1476176使用智能指针,它自己处理内存分配/释放。 – Zereges