默认移动构造函数/赋值和已删除的复制构造函数/赋值

P-G*_*-Gn 20 c++ move-semantics deleted-functions

根据标准,

如果类X的定义没有显式地声明一个移动构造函数,那么当且仅当一个移动构造函数被隐式声明为默认值时

- X没有用户声明的复制构造函数,

- X没有用户声明的复制赋值运算符,

- X没有用户声明的移动赋值运算符,和

- X没有用户声明的析构函数.

现在以下无法编译

# include <utility>

class Foo
{
public:
  Foo() = default;
  Foo(Foo const &) = delete;
};

int main()
{
  Foo f;
  Foo g(std::move(f)); // compilation fails here
  return 0;
}
Run Code Online (Sandbox Code Playgroud)

所以似乎删除的函数被认为是用户定义的,这是有道理的(它不是它的默认实现).但是,在那种特殊情况下,如何删除复制construtor/assignment mess默认移动构造函数/赋值?

我认为这个问题具有实际意义,因为手动生成和特别是 这种默认函数的维护是容易出错的,同时,诸如std::unique_ptr类成员之类的类的使用的(正当的)增加使得不可复制的类比以前更常见.

Hol*_*olt 24

user-declared表示用户提供(由用户定义),显式默认(= default)或显式删除(= delete)与隐式默认/删除(例如移动构造函数)相比.

因此,在您的情况下,隐式删除移动构造函数,因为复制构造函数被显式删除(因此用户声明).

但是,在那种特殊情况下,如何删除复制构造函数/赋值混乱默认移动构造函数/赋值?

它不会,但标准并没有区分这种情况和复杂的情况.

最简单的回答是,有一个隐含定义的移动构造函数与明确删除拷贝构造可能是危险的在某些情况下,同样的,当你有一个用户定义的 析构函数,没有用户定义拷贝构造函数(见三个规则/五/零).现在,您可以争辩说用户定义的析构函数不会删除复制构造函数,但这只是语言中的一个缺陷,无法删除,因为它会破坏很多旧的(坏的)程序.引用Bjarne Stroustrup:

在理想的世界中,我认为我们会将"无代"作为默认值,并为"给我所有常规操作"提供一个非常简单的符号.[...]此外,"无默认操作"政策导致编译时错误(我们应该有一个简单的修复方法),而默认策略生成操作会导致在运行时才能检测到的问题.

您可以在N3174 = 10-0164中阅读更多相关信息.

请注意,大多数人遵循三/五/零规则,在我看来你应该.通过隐式删除默认的move-constructor,标准是"保护"你免于错误,并且在某些情况下删除copy-constructor应该在很长一段时间内保护你(参见Bjarne的论文).

如果您有兴趣,请进一步阅读:

我认为这个问题具有实际意义,因为手动生成和特别是 这种默认函数的维护是容易出错的,同时,诸如std::unique_ptr类成员之类的类的使用的(正当的)增加使得不可复制的类比以前更常见.

将移动构造函数标记为显式默认将解决此问题:

class Foo {
public:
  Foo() = default;
  Foo(Foo const &) = delete;
  Foo(Foo&&) = default;
};
Run Code Online (Sandbox Code Playgroud)

你得到一个带有默认移动构造函数的不可复制对象,在我看来,这些显式声明比隐式声明更好(例如,只通过声明移动构造函数default而不删除复制构造函数).