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

why does adding a move constructor disable initializier list?

本文关键字:初始化 列表 构造函数 添加 移动 为什么      更新时间:2023-10-16

使用简单的struct,例如

struct Foo { int i; };

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

Foo foo { 314 };

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

struct Bar
{
    int i;
    Bar(Bar&& other) { i = other.i; }
};

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

Bar(int i) : i(i) {}

我猜想这种行为与此答案有些相关(对于用户定义的移动 - 构建体禁用隐式复制构建器吗?),但是更多的详细信息会很好。

编辑:如答案所示,这与添加构造函数有关。如果我只是添加一个移动操作员,那似乎会造成各种不一致的情况:

struct Baz
{
    int i;
    Baz& operator=(Baz&& other)
    {
        this->i = other.i;
        return *this;
    }
};

初始化器再次起作用,尽管"移动"的语法略有不同(是的,这实际上是默认的构造和移动分配;但最终结果似乎大致相同):

Baz baz{ 3141 };
Baz b;
b = std::move(baz);

当没有构造函数时,此语法是汇总初始化,因为此结构是 gentregate 。。

添加构造函数后,该结构不再是 contregate 汇总初始化无法使用。确切的规则在列表初始化中列出,相关规则是:

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

  • 否则,如果t是骨料类型,则执行聚合初始化。
  • 否则,在两个阶段考虑T的构造函数:...

不是初始化器列表由移动构造函数所禁用的构建(从那里开始),而是汇总构造。出于一个充分的理由:通过添加自定义构造函数,我们向编译器说明中的类不是汇总中的类,而不仅仅是一个不同的成员在每个成员上进行操作。

对于汇总,即使成员变量具有非平凡类型,默认默认值,复制和移动构造函数也将很好地工作。如果不能将其委派给它们,则将自动删除副本构造,而Move Construction可用:

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
}

但是,如果您想出于某些其他原因删除复制构造并将其他构造默认保留,请明确说明:

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
}

因为您使用的是汇总初始化,其中说:

汇总初始化是列表限制的一种形式, 初始化汇总的聚合是以下类型之一:

的数组类型类类型(通常是结构或联合)
  • 没有私人或受保护的非静态数据成员
  • 没有用户提供, 继承或显式(自C 17)构造函数(明确 允许默认或删除的构造函数)(因为C 11)
  • 否 虚拟,私人或受保护(自C 17)基类
  • 没有虚拟的 成员功能

点2使您的情况失败。