使用简单的struct
,例如
struct Foo { int i; };
我可以使用初始化列表创建一个新实例;无需编写构造函数:
Foo foo { 314 };
如果我现在添加移动构造函数
struct Bar
{
int i;
Bar(Bar&& other) { i = other.i; }
};
初始化程序不再有效,我也必须添加一个构造函数:
Bar(int i) : i(i) {}
我猜这种行为与this answer(user-defined move-constructor disables the implicit copy-constructor?)有些相关,但更多细节会很好。
如答案所示,编辑,这与添加构造函数有关。如果我只添加一个移动运算符,这反过来会产生不一致的排序:
struct Baz
{
int i;
Baz& operator=(Baz&& other)
{
this->i = other.i;
return *this;
}
};
初始化程序再次起作用,虽然“move”的语法略有不同(是的,这实际上是默认构造和移动赋值;但最终结果似乎大致相同):
Baz baz{ 3141 };
Baz b;
b = std::move(baz);
答案 0 :(得分:6)
如果没有构造函数,则此语法为aggregate initialization,因为此结构是聚合。
添加构造函数时,此结构不再是聚合,聚合初始化不能使用。确切的规则列在list initialization中,相关的规则是:
类型T对象的列表初始化的效果是:
- 否则,如果T是聚合类型,则执行聚合初始化。
- 否则,T的构造函数分为两个阶段:......
答案 1 :(得分:3)
因为您使用的是aggregate initialization,其中包含:
聚合初始化是列表初始化的一种形式,其中 初始化聚合聚合是以下类型之一: 数组类型类类型(通常是struct或union),具有
- 没有私人或受保护的非静态数据成员
- 没有用户提供的, 继承或显式(自C ++ 17以来)构造函数(显式 允许默认或删除构造函数)(自C ++ 11起)
- 无 虚拟,私有或受保护(自C ++ 17)基类
- 没有虚拟 成员职能
第2点会使您的案件失败。
答案 2 :(得分:3)
它不是初始化列表构造,它被移动构造函数(在那里开始时不存在)禁用,但是聚合构造。并且有一个很好的理由:通过添加一个自定义构造函数,我们向编译器准确地指出中的类不是聚合,需要一些不同的东西,而不是逐个操作每个成员。
对于聚合,即使成员变量具有非平凡类型,默认的默认,复制和移动构造函数也能正常工作。如果无法委托复制构造,则会自动删除复制构造,从而使移动构造可用:
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
}