【发布时间】:2013-08-12 06:17:27
【问题描述】:
我认为最简单的提问方式是举个例子。假设我们有以下类型:
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;
代码会编译。但我不确定它是否符合我的预期。也许对移动语义和右值引用有深入了解的人可以添加一些 cmets:-)
【问题讨论】:
-
您的解决方案在技术上具有未定义的行为:因为当您将
_children传递给generateChildren时它未初始化,所以循环正在对未初始化的对象调用移动赋值运算符。 (鉴于Node有一个可以简单构造的成员,我承认这种未定义行为的最可能结果是完全按照您的意愿工作。)另外,我看到您的解决方案从 0 开始初始化节点,这并不'不同意代码中从 1 开始的示例。
标签: templates c++11 move-semantics rvalue-reference array-initialization