You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

禁用-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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.08 22:11:05