std::vector::push_back 一个不可复制的对象给出编译器错误 [英] std::vector::push_back a non-copyable object gives compiler error

查看:21
本文介绍了std::vector::push_back 一个不可复制的对象给出编译器错误的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在 g++ (GCC) 4.7.2 上遇到编译错误,但在尝试 std::vector::push_back 时却没有代码> 不可复制(私有复制构造函数)但可移动的对象.对我来说,我的示例看起来与 SO 和其他地方的许多其他示例相同.错误消息使它看起来像结构不是直接可构造"的问题 - 我不知道这意味着什么,所以我非常不确定为什么对象需要直接构造"才能被推回.

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;
}

给出错误

/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 的类)
  • Use MyStruct(const MyStruct&) = delete; instead of private ctor hack
  • Inherit boost::noncopyable (or another class with private ctor)

推荐答案

失败是由于 G++ 4.7 的限制,它没有实现 DR 1170,在 C++11 标准化过程的后期进行了更改,表示访问检查应该作为模板参数推导的一部分进行.

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.

根本原因是 libstdc++ 的 vector 将移动元素,如果移动操作保证不抛出(即声明为 noexceptthrow()),否则如果类型是可复制的,则元素将被复制,否则如果类型不可复制但确实有可能抛出的移动操作,则它将被移动(如果抛出异常,则结果为 undefined 未指定.)这是通过检查 is_nothrow_move_constructibleis_copy_constructible 类型特征来实现的.在您的情况下,该类型不是可构造的,因此检查了 is_copy_constructible 特征.您的类型有一个复制构造函数,但它不可访问,因此 is_copy_constructible 特征会在 G++ 4.7 中产生编译器错误,因为在模板参数推导期间未进行访问检查.

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 undefined 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.

如果你让你的移动构造函数和移动赋值运算符 noexcept 那么类型将被移动并且不需要是可复制的,所以失败的 is_copy_constructible 特征是没用过,代码编译OK.

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.

或者,(如评论中所述)如果您删除了复制构造函数,则 is_copy_constructible 特征会得到正确的结果.

Alternatively, (as also stated in the comments) if you make the copy constructor deleted then the is_copy_constructible trait gets the right result.

另一种选择是使用类似 boost::noncopyable 的东西,它会隐式删除复制构造函数,因此 is_copy_constructible 特征可以正常工作(并且也适用于 MSVC 等较旧的编译器不支持正确删除的功能).我不知道您所说的无法找到错误是什么意思,MSVC 没有向您显示编译器错误的完整上下文吗?

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?

结论:在适当的地方使用 unique_ptr 但不要让类显式可移动

Conclusion: use unique_ptr where appropriate but don't make classes explicitly movable

我不同意这个结论,太极端了.相反,尽可能让你的类不能移动.此外,在可能的情况下,使用已删除的函数来使类型不可复制,而不是私有+未实现的函数,可能使用宏来移植到旧编译器,例如

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 一个不可复制的对象给出编译器错误的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆