我认为最简单的提问方式是通过一个例子。假设我们有以下类型:
class Node
{
// make noncopyable
Node(const Node& ref) = delete;
Node& operator=(const Node& ref) = delete;
// but moveable
Node(Node&& ref) = default;
Node& operator=(Node&& ref) = default;
// we do not have a default construction
Node() = delete;
Node(unsigned i): _i(i) {}
unsigned _i;
};
现在我想将其中一些节点存储在std::array:中
template<unsigned count>
class ParentNode
{
std::array<Node,count> _children;
ParentNode()
// i cannt do this, since i do not know how many nodes i need
// : _children{{Node(1),Node(2),Node(3)}}
: _children() // how do i do this?
{}
};
正如评论中所说,问题是:我该如何做到这一点?传递给子级的无符号应该是存储子级的数组的索引。但我们也非常感谢更通用的解决方案!
我发现自己的以下解决方案可能会导致更复杂类型的未定义行为。要获得一个定义良好的解决方案,请参阅公认答案。
template<unsigned count>
class ParentNode
{
public:
// return by value as this will implicitly invoke the move operator/constructor
std::array<Node,count> generateChildren(std::array<Node,count>& childs)
{
for (unsigned u = 0; u < count; u++)
childs[u] = Node(u); // use move semantics, (correct?)
return std::move(childs); // not needed
return childs; // return by value is same as return std::move(childs)
}
std::array<Node,count> _children;
ParentNode()
// i cannt do this, since i do not know how many nodes i need
// : _children{{Node(1),Node(2),Node(3)}}
: _children(generateChildren(_children)) // works because of move semantics (?)
{}
};
ParentNode<5> f;
代码确实可以编译。但我不确定它是否达到了我期望的效果。也许对移动语义和右值引用有很好见解的人可以添加一些注释:-)
您可以使用variadics生成array
,其中元素初始化为索引的任意函数。使用标准机制生成索引序列:
template <int... I> struct indices {};
template <int N, int... I> struct make_indices :
make_indices<N-1,N-1,I...> {};
template <int... I> struct make_indices<0,I...> : indices<I...> {};
它相当简单:
template <typename T, typename F, int... I>
inline std::array<T, sizeof...(I)> array_maker(F&& f, indices<I...>) {
return std::array<T, sizeof...(I)>{ std::forward<F>(f)(I)... };
}
template <typename T, std::size_t N, typename F>
inline std::array<T, N> array_maker(F&& f) {
return array_maker<T>(std::forward<F>(f), make_indices<N>());
}
这让我们可以做任何事情,从复制std::iota
:的效果
auto a = array_maker<int,10>([](int i){return i;});
将前10个自然数的平方按相反顺序排列:
const auto a = array_maker<std::string,10>([](int i){
return std::to_string((10 - i) * (10 - i));
});
由于Node
是可移动的,因此可以将ParentNode
构造函数定义为:
ParentNode()
: _children(array_maker<Node, count>([](unsigned i){return i+1;}))
{}
在科利鲁现场观看这一切。
实际上,你无能为力。你想把一个没有默认构造函数的类型放入一个由模板参数决定大小的数组中,然后想用一些任意值初始化元素,这让你陷入了困境。
函数不能返回任何内容,该函数可以放入一个支撑的init列表中,并用于初始化具有多个元素的数组(或任何类型的聚合)。{}
并不意味着"initializer_list
"。它是一个支持的init列表,在某些情况下,它可以成为initializer_list
,但它也可以成为构造函数调用的参数或聚合初始化中使用的元素。
您最好的选择是使用vector
,并通过循环手动初始化它。