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

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

问题描述

我在 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没有显示你的编译器错误的完整上下文。


    结论:在适当的情况下使用unique_ptr,但不要让类显式移动


    我不同意这个结论,相反,让你的类不要移动,只要有可能。此外,如果可能,使用删除的函数,使类型不可复制,而不是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 of private 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 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.

    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?

    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天全站免登陆