C++按值返回对象时地址不同?Copy Elision为何未生效?
关于拷贝消除(Copy Elision)的地址差异问题
核心原因分析
1. 命名返回值优化(NRVO)的可选性
你的代码中test()函数返回的是命名局部对象obj1,这属于NRVO的场景。虽然C++17强制了针对临时对象的返回值优化(RVO),但NRVO仍然是编译器的可选优化,而非强制要求:
- 在g默认的
-O0(调试模式)下,编译器为保留调试信息、方便断点调试,通常会禁用NRVO。此时obj1会在test()的栈帧中构造,返回时编译器会利用C17的保证拷贝消除规则,直接将obj1的内容“转移”到main()的obj2位置,但不会复用同一个栈空间,因此两者地址不同。 - 你添加拷贝、移动构造函数后没有输出,是因为编译器确实消除了拷贝/移动操作(符合C++17的保证拷贝消除),但因未启用NRVO,对象存储位置分属两个栈帧,所以地址仍不同。
2. vector地址一致的误解
你提到返回vector时地址一致,大概率是指vector内部堆内存的地址,而非vector对象本身的地址:
vector的底层数据存储在堆上,返回vector时,即使编译器未启用NRVO,移动构造函数也会直接将内部指针转移给目标对象,不会拷贝堆数据,因此内部数据的地址会一致。若打印vector对象本身的地址,在-O0模式下同样会和函数内的vector对象地址不同。
验证方法
若想看到NRVO生效后的地址一致效果,可开启优化编译:
g++ -std=c++20 -O2 your_code.cpp
此时编译器会启用NRVO,直接在main()的obj2位置构造test()中的obj1,两者地址完全相同,同时拷贝/移动构造函数也不会被调用。
补充说明
- 即使未启用NRVO,C++17的保证拷贝消除也会避免拷贝/移动操作的实际执行,这就是你添加构造函数后没有输出的原因。
- 调试模式下的地址差异是编译器为调试便利性做出的选择,并不代表拷贝消除未发生。
内容的提问来源于stack exchange,提问作者Shivanshu Arora
相关产品推荐
相关产品推荐

