昨天我问过以下question,为方便起见,转载于此处;
“对于我的一个项目,我真正想做的就是这样做(将其简化为最低限度);
struct Move
{
int src;
int dst;
};
struct MoveTree
{
Move move;
std::vector<MoveTree> variation;
};
我必须承认,我认为不可能直接这样做,我认为MoveTree中的MoveTree向量将被禁止。但无论如何我都试过了,它的效果非常好。我正在使用Microsoft Visual Studio 2010 Express。
这可移植吗?这是好习惯吗?我有什么值得担心的吗?“
基本上社区的回答是否定的,我不能这样做,标准禁止它,所以它的工作意味着我很幸运。
所以我的新问题是。如何在合法的C ++中实现我想要的简单功能,而不会增加一堆令人讨厌的复杂性和痛苦?
答案 0 :(得分:6)
您需要使用指针和动态分配。你应该使用智能指针,以确保你不泄漏任何东西。 boost::shared_ptr
允许类型不完整,因此这是合法的:
std::vector< boost::shared_ptr<MoveTree> > variation;
(我不知道关于0x std::shared_ptr
TBH,但它应该是相同的。)
答案 1 :(得分:3)
您可以使用Boost Pointer Container Library。它类似于使用std :: vector,但容器拥有指针,因此它会破坏对象。
您可以声明:
#include <boost/ptr_container/ptr_vector.hpp>
struct MoveTree{
Move move;
boost::ptr_vector<MoveTree> variation;
};
现在,如果您想添加新元素,可以使用:
variation.push_back(new MoveTree());
现在,容器拥有指针,您可以通过引用获取它,例如:
variation[i].move.src = ...
当容器被销毁时,对象将被销毁。
答案 2 :(得分:1)
我手边没有C ++标准的副本,所以我无法检查标准,但问题出在这里:
struct foo { foo x; }
是非法的,原因很明显。更一般地说,除了成员函数体之外,您不能在其任何成员中引用结构的大小。struct foo { foo *x; }
完全没问题所以问题是,std::vector
是否定义了直接或间接依赖sizeof(MoveTree)
的任何成员(成员函数除外)?
显然,std::vector
不能拥有MoveTree
本身的静态成员,因为这意味着空向量会调用MoveTree构造函数。然而,人们可以设想std :: vector与单元素向量的对齐char inlineStorage[sizeof(MoveTree)]
优化。撇开这是否会提高性能,目前的问题是标准是否允许实施采取这种方法。
也就是说,出于不同的原因,它仍然是一个坏主意:因为向量在调整存储大小时必须复制它们的元素,所以在向量中使用带有昂贵复制构造函数的元素是个坏主意。这里我们有一个类,其复制构造函数必须以递归方式重新创建整个树。最好使用智能指针类来间接引用子节点以避免这种开销:
std::vector<boost::shared_ptr<MoveTree> > variation;
// in C++0x:
std::vector<std::shared_ptr<MoveTree> > variation;
// in C++0x you can also use for lower overhead:
std::vector<std::unique_ptr<MoveTree> > variation;
// you must then use this pattern to push:
variation.push_back(std::move(std::unique_ptr<MoveTree>(new MoveTree())));
答案 3 :(得分:1)
如果您可以在任何模板中使用不完整类型MoveTree
作为模板参数,那么请使用其他答案中的一个解决方案(例如Cat Plus Plus),或者只使用你的原始解决方案,添加一些沉重的评论,然后再做你的忏悔。
如果您在任何模板参数仍然不完整时无法使用它,则可以使用pimpl idiom解决此问题。
到定义实现类时,MoveTree
类将完成:
struct Move
{
int src;
int dst;
};
struct MoveTreeImpl;
struct MoveTree
{
Move move;
// Todo: Implement RAII here for the impl
// Todo: Provide impl accessor functions here
private:
MoveTreeImpl* impl;
};
struct MoveTreeImpl
{
std::vector<MoveTree> variation;
};
这个解决方案有毛茸茸的部分:
由于您试图避免直接实例化任何类型不完整的模板,因此您必须手动实现RAII。您无法获得std::scoped_ptr<MoveTreeImpl>
的帮助,因为MoveTreeImpl
也不完整。
您的访问者功能有哪些签名?你可以从访问者那里返回std::vector<MoveTree>&
吗?我不确定 - 我们试图避免直接使用MoveTree
的模板。这可能有所不同,因为它不是数据成员,但我不确定:)
修改强>
阅读更多内容,并获得其他用户的回复,似乎很多这些废话是不必要的。只有标准容器才有限制。您可以使用pimpl
实现std:scoped_ptr<MoveTreeImpl>
,我认为从访问者函数返回std::vector<MoveTree>&
,两者都没有问题。
答案 4 :(得分:0)
使用指针(或智能指针)指向Vector中的类型,这将是可移植的。这是因为只需声明您不需要定义就可以完成指向类型的指针。
struct Move
{
int src;
int dst;
};
struct MoveTree;
struct MoveTree
{
Move move;
std::vector<MoveTree*> variation;
};
如果您需要管理类型,请使用可以为您处理删除的智能指针。
原始答案to this question。