C++构造函数处理临时对象异常:两种结构体构造结果差异咨询
为什么两段C++代码的移动构造结果不同?
测试代码定义
#include <iostream> using namespace std; struct Test { int size; Test(int a) { cout << "default" << endl; size = a; } Test(Test const & t) { cout << "copy" << endl; size = t.size + 1; } Test(Test && t) { cout << "move" << endl; size = t.size + 1; } ~Test() { cout << "destruct" << endl; } }; struct Test2 { int size; Test2(int a) { cout << "default2" << endl; size = a; } Test2(Test const & t) { cout << "copy2" << endl; size = t.size + 1; } Test2(Test && t) { cout << "move2" << endl; size = t.size + 1; } ~Test2() { cout << "destruct2" << endl; } };
测试场景1
int main() { Test t{3}; // 输出"default" cout << t.size << endl; // 输出3 Test move_from_temp = Test(Test(std::move(t))); // 输出"move" cout << move_from_temp.size << endl; // 输出4 return 0; }
测试场景2
int main() { Test t{3}; // 输出"default" cout << t.size << endl; // 输出3 Test2 move_from_temp = Test2(Test(std::move(t))); // 依次输出"move"、"move2"、"destruct" cout << move_from_temp.size << endl; // 输出5 return 0; }
问题描述
预期上述两个场景的结果应该一致,但实际输出不同,请问造成差异的原因是什么?
原因分析
核心差异来自C++复制消除(Copy Elision)机制的应用限制:
场景1:触发复制消除
语句Test move_from_temp = Test(Test(std::move(t)));中,所有临时对象的类型都是Test。根据C++标准的复制消除规则(尤其是返回值优化RVO的扩展场景),编译器可以直接将最内层临时对象的构造结果直接初始化move_from_temp,跳过中间的移动构造步骤。实际执行流程简化为:std::move(t)将t转为右值,触发Test(Test&&)构造函数,直接初始化move_from_temp,size变为3+1=4;- 完全跳过外层的
Test(...)临时对象构造,因此只触发一次移动构造,最终move_from_temp.size为4。
场景2:无法触发复制消除
语句Test2 move_from_temp = Test2(Test(std::move(t)));中,内层临时对象是Test类型,外层构造的是Test2类型——两者类型不匹配,编译器无法进行复制消除,必须完整执行所有构造步骤:Test(std::move(t))触发Test(Test&&),构造出size=4的Test临时对象,输出"move";- 用这个
Test右值临时对象触发Test2(Test&&)构造函数,构造move_from_temp,size变为4+1=5,输出"move2"; - 临时
Test对象被析构,输出"destruct";
最终move_from_temp.size为5。
补充:复制消除是C++标准允许的优化,即使会改变构造/析构函数的调用次数(进而改变输出),编译器也有权执行。只有当临时对象和目标对象类型一致时,才有可能触发这类对象构造的合并优化。
内容的提问来源于stack exchange,提问作者vincent
相关产品推荐
相关产品推荐

