禁用-fno-elide-constructors时拷贝构造函数调用次数疑问
C++拷贝构造函数两次调用的原因解析
问题背景
禁用拷贝省略(添加-fno-elide-constructors编译选项)编译给定代码后,输出显示拷贝构造函数被调用两次,但预期应为一次。疑惑该现象是标准规定还是实现细节,且Clang、GCC、MSVC均出现此结果。
代码实现
IntPointerWrapper类定义
struct IntPointerWrapper { int* data_pointer; IntPointerWrapper() : data_pointer(new int()) { std::cout << "IntPointerWrapper()" << std::endl; } IntPointerWrapper(const IntPointerWrapper& other) : data_pointer(new int()) { std::cout << "IntPointerWrapper(const IntPointerWrapper& other)" << std::endl; *data_pointer = *other.data_pointer; } IntPointerWrapper& operator=(const IntPointerWrapper& other) { std::cout << "operator=(const IntPointerWrapper& other)" << std::endl; *data_pointer = *other.data_pointer; return *this; } ~IntPointerWrapper() { std::cout << "~IntPointerWrapper() " << std::endl; delete data_pointer; } };
工厂函数及main函数
IntPointerWrapper bar() { IntPointerWrapper q; return q; } int main() { IntPointerWrapper m = bar(); }
编译输出
IntPointerWrapper() IntPointerWrapper(const IntPointerWrapper& other) ~IntPointerWrapper() IntPointerWrapper(const IntPointerWrapper& other) ~IntPointerWrapper() ~IntPointerWrapper()
原因解答
这是C++标准规定的行为,并非编译器实现细节,具体流程如下:
- 执行
bar()函数时,先构造局部对象q,触发一次默认构造函数调用。 - 函数返回
q时,由于禁用了拷贝省略,编译器必须将q拷贝构造到函数的返回临时对象中——这是第一次拷贝构造调用。 - 局部对象
q完成使命,触发第一次析构函数调用。 - 回到
main()函数,用返回的临时对象拷贝构造m——这是第二次拷贝构造调用。 - 临时对象不再被使用,触发第二次析构函数调用。
- 程序结束时,
m被销毁,触发第三次析构函数调用。
关于C++版本的补充
- 在C++17之前,返回值优化(RVO)属于可选优化,禁用后必须严格执行两次拷贝构造操作。
- C++17引入了“保证拷贝省略”规则,对于
return 局部对象这类场景,编译器必须直接在目标对象(m)的内存空间构造返回值,理论上不会产生临时对象。但-fno-elide-constructors选项会强制编译器模拟无优化时的拷贝行为,因此依然会触发两次拷贝构造调用,这是该选项的设计目的——展示未启用优化时的完整构造/析构流程。
内容的提问来源于stack exchange,提问作者Luckk
相关产品推荐
相关产品推荐

