Ðа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)
移动构造函数禁用的不是初始值设定项列表构造(一开始就不存在),而是聚合构造。并且有一个很好的理由:通过添加自定义构造函数,我们向编译器准确指示该类不是聚合,需要进行一些不同的操作,而不仅仅是一一操作其每个成员。
对于聚合,即使成员变量具有重要类型,默认的默认构造函数、复制构造函数和移动构造函数也能正常工作。如果无法将复制构造委托给他们,则复制构造将自动删除,从而使移动构造可用:
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)