c++ - 为什么删除移动构造函数会导致 vector 停止工作

标签 c++ c++11

如果我在类中禁止移动构造函数,我就不能再在 vector 中使用它:

class Foo
{
  public:
      Foo(int i) : i_(i) {}
      Foo(Foo&&) = delete;

      int i_;
};

int main()
{
    std::vector<Foo> foo;
    foo.push_back(Foo(1));
}

为什么会这样?

最佳答案

总结

不要删除移动成员。


假设您的编译器完全符合 C++11,那么显式删除移动构造函数也将隐式声明以下内容:

Foo(const Foo&) = delete;
Foo& operator=(const Foo&) = delete;

也就是说,如果您声明了一个移动构造函数(或移动赋值运算符),并且没有声明复制成员,它们将被隐式声明为已删除。所以你的完整类 Foo 就好像:

class Foo
{
  public:
      Foo(int i) : i_(i) {}
      Foo(Foo&&) = delete;
      Foo(const Foo&) = delete;             // implicitly declared
      Foo& operator=(const Foo&) = delete;  // implicitly declared

      int i_;
};

现在vector<Foo>::push_back(Foo(1))要求 FooMoveConstructible . MoveConstructible可以通过可访问的移动构造函数来满足,甚至可以通过可访问的复制构造函数来满足。但是Foo两者都没有。要修复你可以:

class Foo
{
  public:
      Foo(int i) : i_(i) {}
      Foo(const Foo&) = default;
      Foo& operator=(const Foo&) = default;

      int i_;
};

即默认复制成员并删除已删除的移动成员。

一般来说,显式删除移动成员并不是一个好主意。如果您希望一个类可复制但不可“移动”,只需像在 C++03 中那样声明:声明/定义您的复制成员。您可以让复制成员由编译器生成 = default ,这仍然算作用户声明。并且不要声明移动成员。不存在的移动成员与已删除的移动成员不同。

删除移动成员意味着你不能构建Foo的拷贝来自右值,即使复制构造函数可以正常工作。这很少是期望的意图。

即使您希望您的类不可复制也不可移动,最好只删除复制成员并保留未声明的移动成员(意味着它们将不存在)。如果您在审查代码(包括您自己的代码)时看到已删除的移动成员,那么它们几乎肯定是不正确的,或者至多是多余和令人困惑的。

总有一天会有人想出一个很好的删除移动成员的用例。但这将是一个罕见的用例。如果您在代码中看到这样的模式,您应该期望代码作者有很好的解释。否则,删除的移动成员可能只是不正确的(充其量是多余的)。但从好的方面来说,这个错误会在编译时出现,而不是在运行时出现(如您的示例所示)。

以下是当您显式声明任何特殊成员时编译器将隐式执行的操作的摘要图表。那些红色方 block 表示已弃用的行为。

enter image description here

= default= delete算作用户声明

Click here如果您想查看完整的幻灯片。

关于c++ - 为什么删除移动构造函数会导致 vector 停止工作,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15181424/

相关文章:

使用 clang 的 C++ 模块

c++ - 无法完成 Rice (Ruby) 的简单示例。我错过了什么?

c++ - 复制构造函数初始化

c++ - Cocos2d-x 3 测试版 : polygon not drawn right

c++ - 接受类参数的友元函数的尾随返回类型

c++ - __thiscall 关键字的用途是什么?

c++ - 在 Qt Creator 中使用 C++11

c++ - 如何从 boost::multiprecision::cpp_int 转换为 cpp_dec_float<0>(而不是 cpp_dec_float_50 等)?

c++ - 使用std::vector创建自定义复数类的vector

c++ - gcc 可以编译可变参数模板而 clang 不能