C++ std::vector::推回一个不可复制的对象会导致编译器错误

C++ std::vector::推回一个不可复制的对象会导致编译器错误,c++,c++11,g++,C++,C++11,G++,当我试图std::vector::push_back一个不可复制(私有复制构造函数)但可移动的对象时,我在g++(GCC)4.7.2上得到编译错误,但在MSVC-2012上没有得到编译错误。在我看来,我的例子和其他地方的许多例子是一样的。错误消息使它看起来像是结构不“直接可构造”的问题——我不知道这意味着什么,所以我无法确定为什么一个对象需要“直接可构造”才能被推回 #include <vector> #include <memory> struct MyStruct

当我试图
std::vector::push_back
一个不可复制(私有复制构造函数)但可移动的对象时,我在
g++(GCC)4.7.2上得到编译错误,但在
MSVC-2012上没有得到编译错误。在我看来,我的例子和其他地方的许多例子是一样的。错误消息使它看起来像是结构不“直接可构造”的问题——我不知道这意味着什么,所以我无法确定为什么一个对象需要“直接可构造”才能被推回

#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;
}
#包括
#包括
结构MyStruct
{
MyStruct(std::unique_ptr p);
MyStruct(MyStruct&other);
MyStruct&operator=(MyStruct&other);
std::唯一的ptr mP;
私人:
//不可复制
MyStruct(const MyStruct&);
MyStruct和operator=(const MyStruct和other);
};
int main()
{
MyStruct s(std::unique_ptr(新int(5));
std::向量v;
auto other=std::move(s);//测试它是否可移动
v、 推回(std::move(other));//编译失败
返回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
/usr/lib/gcc/x86_64-redhat-linux/4.7.2/../../../../../../../include/c++/4.7.2/type_traits:在“struct std::_是直接的_可构造的_impl”的实例化中:
... 剪
/usr/lib/gcc/x86_64-redhat-linux/4.7.2/../../../../../../../../include/c++/4.7.2/bits/stl_vector.h:900:9:void std std::vector::push_back(std::vector::value_type&)[with _tpmystruct=MyStruct;_Alloc=std::分配器;std::vector::value_type=MyStruct]中的必填项
main.cpp:27:33:此处需要
main.cpp:16:5:错误:“MyStruct::MyStruct(constmystruct&)”是私有的
各种答案的简单解决方法:

  • 使用
    MyStruct(constmystruct&)=delete而不是
    private-ctor
    hack
  • 继承
    boost::noncopyable
    (或另一个具有私有构造函数的类)

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

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

如果使移动构造函数和移动赋值运算符
noexcept
移动,则类型将被移动,并且不需要可复制,因此
是可复制的
未使用失败的特征,并且代码编译正常

或者,(也如注释中所述)如果删除复制构造函数,则
是可复制的
特征会得到正确的结果

另一种选择是使用类似于
boost::noncopyable
的东西,这会隐式删除复制构造函数,因此
is\u copy\u 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);
};

这段代码确实应该编译得非常好,听起来像是一些奇怪的SFINAE问题。你能试试
.emplace_-back(std::move(other))
?与
emplace_-back
相同的错误(我已经试过了,还有一些咒语;)如果你只试试
std::unique_-ptr
,会发生什么,尝试
=删除副本成员,而不是将其设置为私有成员。此外,您需要在移动构造函数和移动分配运算符上添加noexcept,否则gcc vector 4.7将拒绝移动任何内容:只是澄清一下:原始代码标准是否符合要求?再次总结——我指的更多的是公司的编码标准(存在不一致的地方!)。在兼顾MSVC和gcc合规性的同时,在避免这些“意外”的风格、难以解决的错误消息(来自
boost::noncopyable
)和令人满意的性能之间取得了平衡。特别是,在MSVC编译器生成符合最新标准的move-ctor之前,它是太多的样板文件,好处太少。代码是有效的,它被拒绝,因为G++4.7无法实现符合标准的
是可复制的
,因为在模板参数推导过程中没有检查访问权限(即,它没有实现)。该代码与G++4.8或Clang++的最新版本配合使用,因为它们实现了DR 1170。DR1170完全改变了游戏规则。我们有许多YYYY类型的特征,由于私有声明的内容而失败。这在文档中总是被称为特征限制。现在看来,在DR1170之后,特征将不会因为访问问题而受到限制?示例:段落
已知问题
此特征无法检测