当我尝试编译以下代码时,我得到Compiler Error C2248:
#include <list>
#include <memory>
using namespace std;
class data
{
public:
static data parse()
{
data d;
data::parse(d);
return d;
}
list<std::unique_ptr<data>> l;
private:
static void parse(data& node)
{ }
};
int main()
{
return 0;
}
为什么呢?我该如何解决这个问题?
注意:使用std::shared_ptr
代替std::unique_ptr
时,我没有问题。
答案 0 :(得分:10)
您需要为您的类型提供移动操作:
data(data&& other)
: l(std::move(other.l))
{
}
data& operator=(data&& other)
{
l = std::move(other.l);
return *this;
}
而且,既然你已经添加了一个用户声明的构造函数,那么你还需要一个用户声明的默认构造函数:
data() { }
我的理解是,根据最终的C ++ 11语言标准,您的代码是正确的。 Visual C ++没有完全实现隐式生成移动操作的最终规范(从Visual C ++ 2012 RC开始)。生成隐式移动操作的规范在标准化过程中很晚就改变了几次。
如果您的类类型C
具有可移动但不可复制的任何数据成员,则Visual C ++将不会生成隐式移动构造函数或移动赋值运算符,并且隐式复制构造函数和复制赋值运算符都是被移动数据成员的存在所抑制。换句话说,如果您想要不使用仅限移动类型,则必须自己为聚合类提供移动操作。
(至少,这是我对编译器实验的理解。)
答案 1 :(得分:5)
首先,VC ++不会自动生成移动ctor并移动赋值运算符,这意味着您需要自己定义它们。
接下来,当您返回局部变量时,编译器首先尝试移动它们,然后才能实际复制它们。但是,要做到这一点,它需要一个移动ctor。由于它没有那个,它尝试通常的副本,并通过生成的副本ctor自动调用std::list
的拷贝构造函数,后者又尝试调用其元素类型的拷贝ctor,它在{{{ 1}}案例。
您需要定义一个适当的移动ctor或不调用std::unique_ptr
复制文件的复制文件(即,制作内容的深层副本)。
答案 2 :(得分:2)
简答:(特定于C ++ 11)list
中的项目必须是可复制的或可移动的。 unique_ptr
不能通过设计进行复制,但只要受控类型也可移动,它就是可移动的。
您的类型data
不可移动,因为您尚未实现移动语义,并且编译器没有为您执行此操作。
实施移动语义,您可以在unique_ptr
中使用list
:
data(ddata&&) {};
根据标准,编译器将为您的类生成移动构造函数。但是,VS10不支持这一点 - 这可能是你遇到的问题。
如需进一步参考,请参阅我在CR上的帖子:Canonical Implementation of Move Semantics