MSVC中对象构造与成员函数内同指针检查结果矛盾问题问询
MSVC中对象地址检查的矛盾问题分析
以下是用于复现Visual Studio C++编译器问题的最简代码:
struct P { const P * s = nullptr; constexpr P() {} constexpr P(const P & p) : s(&p) { if (s == this) throw 0; // 从未触发 } constexpr bool operator()() const { return s != this; // 按逻辑应始终为true? } }; constexpr bool f(P p) { return p.s ? p() : f(p); } int main() { static_assert( f(P{}) ); // 在MSVC中失败,实际返回false }
问题现象
P类的拷贝构造函数会记录源对象的指针s,构造时的s == this检查永远不会触发,说明拷贝后的新对象地址与源对象地址不同;但在operator()中执行相同的地址检查,却返回s == this的结果,导致static_assert断言失败。
原因分析
这是MSVC的constexpr求值实现bug:
在constexpr上下文处理值传递的参数拷贝时,MSVC错误地将拷贝构造生成的新对象地址与源对象地址视为同一地址。
具体流程中:
- 调用
f(P{})时,初始传入的p是默认构造的对象,p.s为nullptr,因此会递归调用f(p),此时需要拷贝构造一个新的P对象。 - 拷贝构造时,
s被赋值为源对象的地址,此时this指向新构造的对象,显然s != this,所以构造函数中的throw不会触发。 - 但MSVC在constexpr求值过程中,错误地将拷贝后对象的
this指针与源对象的this指针判定为相同,导致operator()中s == this成立,最终返回false,触发断言失败。
而GCC、Clang等编译器的constexpr求值逻辑符合C++标准,能够正确区分拷贝前后的不同对象地址,因此operator()会返回预期的true,断言通过。
内容的提问来源于stack exchange,提问作者Fedor
相关产品推荐
相关产品推荐

