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

构造函数保存的this指针值异常?复制消除与平凡可拷贝类型问题

C++复制消除失效与平凡可复制类型的关联问题

问题代码

#include <cassert>

struct S* ps;

struct S{
    S(){
        ps = this;
    }
    //~S(){} //(*)
};

S makeS(){
    return S{};
}

int main(){
    S s{makeS()};
    assert(&s == ps);
}

问题现象

按照C++的复制消除规则,断言理应成功,但在GCC 13.1和Clang 16.0中以-std=c++20编译时,断言会失败,提升优化等级也无法解决。如果取消(*)处的注释,添加用户自定义的析构函数,断言则不再失败。

原因分析

核心在于**平凡可复制类型(trivially copyable type)**的特性与编译器优化逻辑的交互:

  1. 无自定义析构函数时的情况
    当S没有用户提供的析构函数时,它属于C++标准定义的平凡可复制类型。对于这类类型,标准允许编译器使用memcpy式的内存复制来替代语义上的复制/移动构造操作——即使复制消除(如返回值优化RVO)是允许的,但编译器并非必须执行复制消除。
    在这个例子中:

    • makeS()内构造的临时对象会调用构造函数,将ps指向临时对象的地址;
    • 初始化main中的s时,编译器直接把临时对象的内存复制到s的内存空间,不会触发移动/复制构造函数,自然也不会更新ps的指向;
    • 最终ps指向临时对象的地址,而s的地址不同,断言失败。
  2. 添加自定义析构函数后的情况
    一旦添加了用户提供的析构函数,S就不再是平凡可复制类型。此时编译器无法再用内存复制替代对象的构造语义,必须严格遵循对象的创建逻辑:
    编译器会执行返回值优化(RVO),直接在main中s的内存空间上构造makeS()返回的对象。构造函数中ps = this会直接指向s的地址,断言自然成功。

关键标准依据

C++标准规定,平凡可复制类型的对象可以通过内存复制(如memcpy)来完成复制,其效果与调用复制构造函数完全等价。因此编译器选择内存复制而非复制消除,是符合标准的行为,并非编译器Bug。

内容的提问来源于stack exchange,提问作者Weijun Zhou

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.17 06:05:19