我在g++ (GCC) 4.7.2
上遇到编译错误,但在MSVC-2012
尝试std::vector::push_back
不可复制(私有复制构造函数)但是可移动对象时却没有。对我来说,我的例子看起来与SO和其他地方的许多其他例子相同。错误消息使得它看起来像结构不是“直接可构造”的问题 - 我不知道这意味着什么所以我更加不确定为什么一个对象需要'直接可构造'才能被推回。
#include <vector>
#include <memory>
struct MyStruct
{
MyStruct(std::unique_ptr<int> p);
MyStruct(MyStruct&& other);
MyStruct& operator=(MyStruct&& other);
std::unique_ptr<int> mP;
private:
// Non-copyable
MyStruct(const MyStruct&);
MyStruct& operator=(const MyStruct& other);
};
int main()
{
MyStruct s(std::unique_ptr<int>(new int(5)));
std::vector<MyStruct> v;
auto other = std::move(s); // Test it is moveable
v.push_back(std::move(other)); // Fails to compile
return 0;
}
给出错误
/usr/lib/gcc/x86_64-redhat-linux/4.7.2/../../../../include/c++/4.7.2/type_traits: In instantiation of ‘struct std::__is_direct_constructible_impl<MyStruct, const MyStruct&>’:
... snip ...
/usr/lib/gcc/x86_64-redhat-linux/4.7.2/../../../../include/c++/4.7.2/bits/stl_vector.h:900:9: required from ‘void std::vector<_Tp, _Alloc>::push_back(std::vector<_Tp, _Alloc>::value_type&&) [with _Tp = MyStruct; _Alloc = std::allocator<MyStruct>; std::vector<_Tp, _Alloc>::value_type = MyStruct]’
main.cpp:27:33: required from here
main.cpp:16:5: error: ‘MyStruct::MyStruct(const MyStruct&)’ is private
各种答案的简单解决方法:
MyStruct(const MyStruct&) = delete;
代替private ctor
hack boost::noncopyable
(或其他具有私人ctor的课程)答案 0 :(得分:13)
失败是由于G ++ 4.7的限制,它没有实现DR 1170,这在C ++ 11标准化过程中很晚才改变,说访问检查应该作为模板的一部分完成论证演绎。
根本原因是libstdc ++的vector
将移动元素,如果移动操作保证不抛出(即它声明为noexcept
或throw()
),否则如果类型是可复制的元素将被复制,否则如果类型不可复制但具有可能抛出的移动操作,那么它将被移动(如果抛出异常,结果是 undefined 未指定。)这是实现的检查is_nothrow_move_constructible
和is_copy_constructible
类型特征。在您的情况下,类型不是不可移动构造,因此检查is_copy_constructible
特征。你的类型有一个拷贝构造函数,但是它不可访问,因此is_copy_constructible
特征会产生G ++ 4.7的编译器错误,因为在模板参数推断期间没有进行访问检查。
如果你制作移动构造函数并移动赋值运算符noexcept
,那么类型将被移动并且不需要是可复制的,因此不使用失败的is_copy_constructible
特征,并且代码编译确定。
或者,(如评论中所述)如果您删除了复制构造函数,那么is_copy_constructible
特征会得到正确的结果。
另一个替代方法是使用类似boost::noncopyable
的内容,它隐式地使复制构造函数被删除,以便is_copy_constructible
特征正常工作(并且也适用于不支持已删除函数的MSVC之类的旧编译器) 。我不知道你的意思是什么让它找不到错误,MSVC没有向你显示编译器错误的完整上下文吗?
结论:在适当的地方使用unique_ptr但不要使类显式移动
我不同意这个结论,这太极端了。相反,尽可能让你的课程无法移动。此外,如果可能,使用已删除的函数使类型不可复制而不是私有+未实现的函数,可能使用宏来移植到较旧的编译器,例如。
#if __cplusplus >= 201103L
#define NONCOPYABLE(TYPE) \
TYPE(const TYPE&) = delete; TYPE& operator=(const TYPE&) = delete
#else
// must be used in private access region
#define NONCOPYABLE(TYPE) \
TYPE(const TYPE&); TYPE& operator=(const TYPE&)
#endif
struct MyStruct
{
...
private:
NONCOPYABLE(MyStruct);
};