C++传入初始化列表直接初始化时调用移动还是拷贝构造函数?
对应测试代码
class P // 带编译器自动生成的拷贝构造、移动构造 { public: P(int x, int y) { } }; int main() { // 原片段省略x、y定义,补充后可正常编译验证 int x = 0, y = 0; P p({x,y}); }
结论
在不考虑任何编译优化、严格走C++标准非优化语义的前提下,用{x,y}隐式生成的P类临时对象初始化p时,调用的是编译器自动生成的移动构造函数,不会调用拷贝构造函数。
语义流程拆解
我们可以把P p({x,y});的执行逻辑按标准规则拆成两步:
- 首先处理括号内的初始化器:花括号包裹的
{x,y}是花括号初始化列表,会匹配P类接收两个int参数的构造函数P(int, int),构造出一个P类型的纯右值临时对象。 - 接下来用这个纯右值临时对象作为实参,初始化同类型的变量
p。这一步走重载决议时,因为实参是右值,参数类型为P&&的移动构造函数的匹配优先级,远高于参数类型为const P&的拷贝构造函数,因此编译器会自动选择移动构造完成p的初始化。
常见误区:不少人会误以为同类型临时对象初始化变量一定走拷贝构造,这是对重载决议规则的误解。只要类存在可用的移动构造(不管是用户自定义还是编译器自动生成),用右值初始化同类型对象时都会优先选择移动构造,和这个右值是隐式生成的临时对象还是显式写的
std::move()转换结果没有关系。
关于编译优化的补充说明
你提到的“编译器把临时对象直接构造为目标对象p”的优化,就是C++中的拷贝消除(copy elision)机制:
- 在C++17及之后的标准版本中,这种纯右值初始化同类型变量的场景,拷贝消除是标准强制要求的行为,不是可选优化——也就是说这时候根本不会生成中间临时对象,会直接在
p的内存空间上调用P(int, int)构造函数,连移动构造的调用都不会发生。 - 如果是C++17之前的标准版本,或者编译时手动关闭了拷贝消除(比如GCC/Clang添加
-fno-elide-constructors编译参数),才会出现前面说的“先构造临时对象、再调用移动构造初始化p”的流程。
内容的提问来源于stack exchange,提问作者CPPL
相关产品推荐
相关产品推荐

