10

尝试使用不可复制(私有复制构造函数)但可移动的对象时,出现编译错误g++ (GCC) 4.7.2但未出现编译错误。对我来说,我的示例看起来与 SO 和其他地方的许多其他示例相同。错误消息使它看起来像结构不是“直接可构造”的问题 - 我不知道这意味着什么,所以我不确定为什么一个对象需要“直接构造”才能被推回。MSVC-2012std::vector::push_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破解
  • 继承boost::noncopyable(或另一个具有私有 ctor 的类)
4

1 回答 1

15

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

根本原因是vector如果移动操作保证不会抛出(即声明为noexceptor throw()),libstdc++ 将移动元素,否则如果类型是可复制的,则元素将被复制,否则如果类型不可复制但确实有可能-抛出 move 操作然后它将被移动(如果抛出异常,则结果未定义未指定。)这是通过检查is_nothrow_move_constructibleis_copy_constructible类型特征来实现的。在您的情况下,该类型不是 nothrow move 可构造的,因此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);
};
于 2012-12-10T22:50:30.803 回答