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

12

研究了一下后,我发现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{});
}

在 MSVC 上编译失败,错误信息如下:

xmemory0(600): error C2280: 'Copyable::Copyable(Copyable &&)' : 尝试引用已删除的函数

在 Clang 上也有问题(在线样例):

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

两种情况下,当我删除了显式删除的移动构造/赋值方法后,代码可以编译。据我所知,当你声明复制构造/赋值方法时,编译器不会隐式声明相应的移动成员。所以它们仍然被有效地删除,是吗?为什么当我删除显式删除的移动构造/赋值函数时,代码可以编译?

通常有哪些好的解决方案来解决这个 C++11 的缺陷?我不希望我的对象可移动(但可复制)。


实现通过复制移动? - Neil Kirk
此外,与之相关的是:为什么C++11中删除的函数会参与重载决议? - Nawaz
2个回答

15

删除一个函数并不等同于不声明它。

已删除的函数被声明并参与重载决议,但如果你试图调用它,会产生一个错误。

如果你没有声明移动构造函数,编译器将不会创建该函数,因为你已经创建了一个拷贝构造函数。在右值上进行重载决议将找到你的拷贝构造函数,这也是你想要的。

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

你上述问题中唯一让事情变得更加混淆的部分是,特殊成员函数是否会自动创建取决于稍微晦涩的规则。


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

除非你是移动语义方面的专家(我指的是真正有经验的),否则不要删除特殊移动成员。这样做不会达到你的想法。如果你检查别人已经做过这件事的代码,请指出问题所在。解释必须非常充分,而不能仅仅是“因为我不希望类型移动”。

真的,不要这样做。

实现你想要的正确方式是简单地声明/定义复制成员。移动成员将被隐式禁止(不是被删除,而是没有被创建)。只需编写 C ++98/03。

更多详情请参见此答案


1
+1 这是实用的建议,尽管有很多有用的参考资料,但很难找到。 - Shafik Yaghmour
4
写一本小书如何?把你在SO上多年来整理的所有好东西(移动语义、参数传递、类设计、日期、排列和线程)都放进去,就像在libc++中使用的那样。正如@ShafikYaghmour所说,这些内容很难找到,而且分散在各处。 - TemplateRex
1
@ TemplateRex:寻找时间... - Howard Hinnant

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接