问题描述
我在 g ++(GCC)4.7.2
上遇到编译错误,但在 c> 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:
//不可复制
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)); //无法编译
return 0;
}
提供错误
/usr/lib/gcc/x86_64-redhat-linux/4.7.2 /../../../../ include / c ++ / 4.7.2 / type_traits:In实例化'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:需要从'void std :: vector< _Tp,_Alloc> :: push_back(std ::向量< _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&)'是私人的
- 使用
MyStruct(const MyStruct&)= delete;
$ c> private ctor hack - 继承
boost :: noncopyable
a href =http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#1170> DR 1170 ,这在C ++ 11标准化中发生了很大的变化过程中,访问检查应该作为模板参数推导的一部分。
基本原因是libstdc ++的向量
将移动元素,如果移动操作被保证不抛出(即它声明 noexcept
或 throw()
),否则如果类型是可复制的元素将被复制,否则如果类型不可复制,但有可能抛出的移动操作,那么它将被移动(如果抛出异常,结果是未指定)。这是通过检查 is_nothrow_move_constructible
和 is_copy_constructible
类型特征。在你的情况下,类型不是不可移动构造的,因此检查 is_copy_constructible
trait。你的类型有一个复制构造函数,但它不可访问,因此 is_copy_constructible
trait会在G ++ 4.7中产生一个编译错误,因为访问检查不是在模板参数推导过程中完成的。
如果你使你的移动构造函数和移动赋值运算符 noexcept
,那么类型将被移动,不需要是可复制的,因此不会使用失败的 is_copy_constructible
trait,并且代码会编译成OK。
在注释中)如果你删除了复制构造函数,然后 is_copy_constructible
trait获得正确的结果。
是使用类似 boost :: noncopyable
的东西,隐含地删除了复制构造函数,所以 is_copy_constructible
trait可以正常工作也可以与旧的编译器,如MSVC不支持删除的功能正常)。我不知道你的意思是什么使它不可能找到的错误,MSVC没有显示你的编译器错误的完整上下文。
我不同意这个结论,相反,让你的类不要移动,只要有可能。此外,如果可能,使用删除的函数,使类型不可复制,而不是private +未实现的函数,也许使用宏可移植到旧的编译器,例如
#if __cplusplus> = 201103L
#define NONCOPYABLE(TYPE)\
TYPE(const TYPE&)= delete; TYPE&在私有访问区域中必须使用
#define NONCOPYABLE(TYPE)\
TYPE(const TYPE&) ; TYPE& operator =(const TYPE&)
#endif
struct MyStruct
{
...
private:
NONCOPYABLE(MyStruct);
};
I get compilation errors on g++ (GCC) 4.7.2
but not on MSVC-2012
when trying to std::vector::push_back
a non-copyable (private copy constructor) but moveable object. To me my example looks identical to many other examples on SO and elsewhere. The error message makes it looks like a problem with the struct not being 'direct constructible' - I don't know what this means so am doubly unsure about why an object needs to be 'direct constructible' to be pushed back.
#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;
}
Gives errors
/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
Simple workaround from various answers:
- Use
MyStruct(const MyStruct&) = delete;
instead ofprivate ctor
hack - Inherit
boost::noncopyable
(or another class with private ctor)
The failure is due to a limitation of G++ 4.7, which doesn't implement DR 1170, which was changed very late in the C++11 standardisation process to say that access checking should be done as part of template argument deduction.
The underlying cause is that libstdc++'s vector
will move elements if the move operation is guaranteed not to throw (i.e. it's declared noexcept
or throw()
), otherwise if the type is copyable the elements will be copied, otherwise if the type is not copyable but does have a possibly-throwing move operation then it will be moved (and if an exception is thrown the results are unspecified.) This is implemented with checks to the is_nothrow_move_constructible
and is_copy_constructible
type traits. In your case, the type is not nothrow move constructible, so the is_copy_constructible
trait is checked. Your type has a copy constructor but it's not accessible, so the is_copy_constructible
trait produces a compiler error with G++ 4.7 because access checking is not done during template argument deduction.
If you make your move constructor and move assignment operator noexcept
then the type will be moved and doesn't need to be copyable, so the is_copy_constructible
trait that fails is not used, and the code compiles OK.
Alternatively, (as also stated in the comments) if you make the copy constructor deleted then the is_copy_constructible
trait gets the right result.
Another alternative is to use something like boost::noncopyable
which implicitly makes the copy constructor deleted so the is_copy_constructible
trait works properly (and also works with older compilers like MSVC that don't support deleted functions properly). I don't know what you mean about making it impossible to find the error, does MSVC not show you the full context of a compiler error?
I disagree with this conclusion, it is too extreme. Instead make your classes nothrow movable whenever possible. Also, when possible, use deleted functions to make a type non-copyable instead of private+unimplemented functions, maybe using a macro for portability to older compilers e.g.
#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);
};
这篇关于std :: vector :: push_back一个非可复制对象给出编译器错误的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!