struct X {
X() {}
X(X&&) { puts("move"); }
};
X x = X();
在C++14中,即使移动构造函数有副作用,也可以省略移动(或复制)操作,这得益于[class.copy]/31:
在C++17中,这个规则被删除了。相反,移动操作可以通过[dcl.init]/17.6.1保证被省略:在以下情况下,允许省略复制/移动操作... 当一个未绑定到引用(12.2)的临时类对象将被复制/移动到具有相同cv-限定类型的类对象时
到目前为止,我所陈述的事实都是众所周知的。但现在让我们修改代码,使其读取:如果初始化表达式是prvalue,并且源类型的cv-限定版本与目标类型的类相同,则使用初始化表达式来初始化目标对象。[例子:
T x = T(T(T()));
调用T
默认构造函数来初始化x
. — 结尾 例子]
X x({});
在C++14中,将执行重载决议,并使用默认构造函数将
{}
转换为类型为X
的临时变量,然后将其移动到x
中。复制省略规则允许省略此移动。在C++17中,重载决议相同,但现在[dcl.init]/17.6.1不适用,并且来自C++14的项目已经不存在了。由于初始化程序是花括号初始化列表,因此没有初始化程序表达式。代替它的是[dcl.init]/(17.6.2):
否则,如果初始化是直接初始化,或者如果它是副本初始化,其中源类型的cv非限定版本是与目标类相同或派生类,则考虑构造函数。应枚举适构造函数用的(16.3.1.3),并通过重载决议(16.3)选择最佳构造函数。选定的构造函数被调用以初始化对象,并以其参数作为初始化程序表达式或表达式列表。如果没有构造函数适用,或者重载决议是模糊的,则初始化无效。
这似乎需要调用移动构造函数,如果标准中有其他规则可以省略它,我就不知道在哪里了。
{}
被转换为类型为X
的临时对象......然后绑定到移动构造函数的引用上。我认为你最初的引用不适用。 - Barry