为什么删除的移动语义会导致std :: vector出现问题?

voi*_*ter 10 c++ c++11

在做了一些研究之后,我发现C++ 11的分配器存在缺陷,需要类型可移动/可复制.我确定这是导致这个问题的原因,但我对删除和未声明的移动语义之间的行为感到困惑.

我有以下代码无法在MSVC12和Clang上编译:

#include <vector>

class Copyable
{
public:
   Copyable() = default;

   Copyable(Copyable const& other)
      : m_int(other.m_int)
   {}

   Copyable& operator= (Copyable const& other)
   {
      m_int = other.m_int;
      return *this;
   }

   Copyable(Copyable&&) = delete;
   Copyable& operator= (Copyable&&) = delete;

private:
   int m_int = 100;
};

int main()
{
   std::vector<Copyable> objects;
   objects.push_back(Copyable{});
}
Run Code Online (Sandbox Code Playgroud)

这无法在MSVC上编译:

xmemory0(600):错误C2280:'可复制::可复制(可复制&&)':尝试引用已删除的函数

而对于Clang(现场样本):

new_allocator.h:120:23:错误:调用'Copyable'的已删除构造函数

在这两种情况下,当我删除显式删除的移动构造/分配方法时,代码编译.AFAIK在声明复制赋值/构造方法时,编译器不会隐式声明相应的移动成员.所以它们仍应被有效删除,对吧?当我删除move construct/assign的显式删除时,为什么代码会编译?

一般来说,这个C++ 11缺陷有什么好的解决方法?我不希望我的物体可以移动(但它们是可复制的).

Yak*_*ont 14

删除功能与不声明功能不同.

声明已删除的函数并参与重载解析,但如果您尝试调用它,则会产生错误.

如果未能声明移动构造函数,编译器将不会在创建复制构造函数时创建一个.rvalue上的重载分辨率将找到您的复制构造函数,这可能是您想要的.

你跟你说的foo(foo&&)=delete是"如果有人试图移动构造这个对象,就会产生错误".

我可以在这里说明不同之处:

void do_stuff( int  x ) { std::cout << x << "\n"; }
void do_stuff( double ) = delete;

void do_stuff2( int  x ) { std::cout << x << "\n"; }
//void do_stuff2( double ) = delete;

int main() {
  do_stuff(3); // works
  //do_stuff(3.14); // fails to compile
  do_stuff2(3); // works
  do_stuff2(3.14); // works, calls do_stuff2(int)
}
Run Code Online (Sandbox Code Playgroud)

上述问题的唯一部分使得这一点更加混乱,特殊的成员函数是根据略有神秘的规则自动创建的.


How*_*ant 10

Copyable(Copyable&&) = delete;
Copyable& operator= (Copyable&&) = delete;
Run Code Online (Sandbox Code Playgroud)

除非你是在移动语义方面的专家(我的意思是,真正懂行),永远不会删除特招会员.它不会做你想要的.如果您查看已执行此操作的其他人的代码,请将其调出.解释必须非常扎实,而不是"因为我不希望这种类型移动".

只是不要这样做.

做你想做的事的正确方法是简单地声明/定义你的副本成员.移动成员将被隐式禁止(不删除,但实际上不存在).只需编写C++ 98/03.

有关详细信息,请参阅此答案.

  • 这些年来你在SO(并在libc ++中使用)放在一起的所有这些伟大的东西(移动语义,参数传递,类设计,日期,排列和线程)写一本小书怎么样?正如@ShafikYaghmour正确地指出的那样,它很难找到并且分散. (2认同)