为什么添加移动构造函数禁用初始化列表?

Ðаn*_*Ðаn 5 c++ move initializer-list

用简单的struct

struct Foo { int i; };
Run Code Online (Sandbox Code Playgroud)

我可以使用初始化列表创建一个新实例; 无需编写构造函数:

Foo foo { 314 };
Run Code Online (Sandbox Code Playgroud)

如果我现在添加一个移动构造函数

struct Bar
{
    int i;
    Bar(Bar&& other) { i = other.i; }
};
Run Code Online (Sandbox Code Playgroud)

初始化器不再起作用,我也必须添加一个构造函数:

Bar(int i) : i(i) {}
Run Code Online (Sandbox Code Playgroud)

我猜这种行为与这个答案有些相关(对于用户定义的move-constructor禁用隐式拷贝构造函数?),但更多细节会很好.

编辑:如答案所示,这与添加构造函数有关.如果我只添加一个移动运算符,这反过来会产生不一致的排序:

struct Baz
{
    int i;

    Baz& operator=(Baz&& other)
    {
        this->i = other.i;
        return *this;
    }
};
Run Code Online (Sandbox Code Playgroud)

初始化程序再次起作用,虽然"move"的语法略有不同(是的,这实际上是默认构造和移动赋值;但最终结果似乎大致相同):

Baz baz{ 3141 };
Baz b;
b = std::move(baz);
Run Code Online (Sandbox Code Playgroud)

Max*_*kin 6

当没有构造函数时,此语法是聚合初始化,因为此结构是聚合.

添加构造函数时,此结构不再是聚合,不能使用聚合初始化.列表初始化中列出了确切的规则,相关的规则是:

类型T对象的列表初始化的效果是:

  • 否则,如果T是聚合类型,则执行聚合初始化.
  • 否则,T的构造函数分为两个阶段:......


The*_*Vee 4

移动构造函数禁用的不是初始值设定项列表构造(一开始就不存在),而是聚合构造。并且有一个很好的理由:通过添加自定义构造函数,我们向编译器准确指示该类不是聚合,需要进行一些不同的操作,而不仅仅是一一操作其每个成员。

对于聚合,即使成员变量具有重要类型,默认的默认构造函数、复制构造函数和移动构造函数也能正常工作。如果无法将复制构造委托给他们,则复制构造将自动删除,从而使移动构造可用:

struct A { // non-copyable
  int a;
  int b;
  A(int a_, int b_): a(a_), b(b_) { std::cout << "A(int,int)\n"; }
  A() { std::cout << "A()\n"; }
  A(const A&) = delete;
  A(A&&) { std::cout << "A(A&&)\n"; }
};

struct B {
  A a;
};

int main() {
  B b1{{1,2}}; // OK: aggregate
  B b2{std::move(b1)}; // OK: calls A::A(A&&)
  //B b3{b1}; // error: B::B(const B&) auto-deleted
}
Run Code Online (Sandbox Code Playgroud)

但是,如果您出于其他原因想要删除复制构造并将其他保留为默认值,只需明确说明即可:

struct A { // copyable
  int a;
  int b;
  A(int a_, int b_): a(a_), b(b_) { std::cout << "A(int,int)\n"; }
  A() { std::cout << "A()\n"; }
  A(const A&) { std::cout << "A(const A&)\n"; }
  A(A&&) { std::cout << "A(A&&)\n"; }
};

struct B { // non-copyable
  A a;
  B() = default;
  B(const B&) = delete;
  B(B&&) = default;
};

int main() {
  B b1{{1,2}}; // OK: still an aggregate
  B b2{std::move(b1)}; // delegates to A::A(A&&)
  //B b3{b1}; // error
}
Run Code Online (Sandbox Code Playgroud)