构造函数保存的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)**的特性与编译器优化逻辑的交互:
无自定义析构函数时的情况
当S没有用户提供的析构函数时,它属于C++标准定义的平凡可复制类型。对于这类类型,标准允许编译器使用memcpy式的内存复制来替代语义上的复制/移动构造操作——即使复制消除(如返回值优化RVO)是允许的,但编译器并非必须执行复制消除。
在这个例子中:makeS()内构造的临时对象会调用构造函数,将ps指向临时对象的地址;- 初始化
main中的s时,编译器直接把临时对象的内存复制到s的内存空间,不会触发移动/复制构造函数,自然也不会更新ps的指向; - 最终
ps指向临时对象的地址,而s的地址不同,断言失败。
添加自定义析构函数后的情况
一旦添加了用户提供的析构函数,S就不再是平凡可复制类型。此时编译器无法再用内存复制替代对象的构造语义,必须严格遵循对象的创建逻辑:
编译器会执行返回值优化(RVO),直接在main中s的内存空间上构造makeS()返回的对象。构造函数中ps = this会直接指向s的地址,断言自然成功。
关键标准依据
C++标准规定,平凡可复制类型的对象可以通过内存复制(如memcpy)来完成复制,其效果与调用复制构造函数完全等价。因此编译器选择内存复制而非复制消除,是符合标准的行为,并非编译器Bug。
内容的提问来源于stack exchange,提问作者Weijun Zhou
相关产品推荐
相关产品推荐

