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

时间:2014-10-21 14:59:08

标签: c++ c++11

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

我有以下代码无法在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{});
}

无法在MSVC上编译:

  

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

关于Clang(live sample):

  

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

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

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

2 个答案:

答案 0 :(得分:14)

删除函数与不声明函数不同。

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

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

您对foo(foo&&)=delete的评价是&#34;如果有人试图移动构造此对象,则会生成错误&#34;。

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

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

唯一有上述问题的部分会让人感到更加困惑,因为特殊的成员函数会自动创建或不基于一些神秘的规则。

答案 1 :(得分:10)

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

除非你是移动语义方面的专家(我的意思是,真的知识渊博),否则永远不要删除特殊移动成员。它不会做你想做的事。如果您查看其他人执行此操作的代码,请将其调出。解释必须非常可靠,而不是&#34;因为我不希望类型移动。&#34;

不要这样做。

执行所需操作的正确方法是简单地声明/定义复制成员。移动成员将被隐式禁止(不删除,但实际上不存在)。只需编写C ++ 98/03。

有关详细信息,请see this answer