如果我在类中禁止移动构造函数,我就不能再在 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))
要求 Foo
是MoveConstructible
. 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 表示已弃用的行为。
= default
和 = delete
算作用户声明。
Click here如果您想查看完整的幻灯片。
关于c++ - 为什么删除移动构造函数会导致 vector 停止工作,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15181424/