默认operator==下x≠x的C++程序是否合法?f()必须为true吗?
问题描述
案例一:包含A成员的结构体B
以下程序在GCC中static_assert断言失败,说明常量求值时x != x为true,其中operator==由编译器默认生成:
struct A { int i = 0; constexpr operator int() const { return i; } constexpr operator int&() { return ++i; } }; struct B { A a; bool operator==(const B &) const = default; }; constexpr bool f() { B x; return x == x; } static_assert( f() ); // GCC中失败
案例二:继承自A的结构体B
修改结构体B使其继承自A而非包含A成员后,程序在GCC中运行成功,但在Clang中断言失败:
struct B : A { bool operator==(const B &) const = default; }; constexpr bool f() { B x; return x == x; } static_assert( f() ); // Clang中失败
请问:这两段程序是否均为良构程序,且f()的求值结果必须为true?
分析与结论
这两段程序均为良构程序,但根据C++标准规则,f()的求值结果并非必须为true,编译器的行为差异源于默认生成operator==时的比较逻辑实现,以及重载解析的细节处理,核心原因是A的重载转换运算符在常量求值中引入了副作用。
案例一(B包含A成员)
默认生成的B::operator==会逐成员比较x.a与x.a,此时重载解析的差异导致结果不同:
- 右侧
x.a处于const成员函数的上下文,是const A&,匹配const operator int(),返回i的初始值0。 - 左侧
x.a是非const左值,重载解析会优先选择非const的operator int&(),该转换会执行++i(此时i变为1),最终被隐式转为int值1。 - 两者比较结果为
1 == 0,即false,这是GCC断言失败的原因,且该行为符合标准规则。
案例二(B继承自A)
默认生成的B::operator==会先比较基类子对象:
- GCC认为
const B&的基类子对象是const A&,只能匹配const operator int(),两次转换都返回0,结果为true。 - Clang在重载解析时,可能对基类子对象的
const属性判断不同,触发了带副作用的operator int&(),导致两次转换后的值不同,结果为false。 - 两种编译器的行为均未违反标准,属于规则允许的实现差异。
标准核心依据
C标准规定:默认生成的相等运算符会逐成员/基类子对象执行==比较,重载解析会根据操作数的const限定选择最优匹配的转换运算符。由于A的两个转换运算符一个无副作用、一个带++i的副作用,不同上下文的解析结果可能导致比较值不同,而这类带副作用的转换在C14及以后的常量表达式中是合法的,因此程序本身良构,但结果不固定。
内容的提问来源于stack exchange,提问作者Fedor
相关产品推荐
相关产品推荐

