这个节目
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() ); // fails in GCC
在 GCC 编译器中静态断言失败,这意味着
x != x
在持续评估期间是 true
,其中 operator==
由编译器生成。在线演示:https://gcc.godbolt.org/z/4xjTnM54M
如果修改
struct B
使其继承自 A
而不是将其作为字段:
struct B : A {
bool operator==(const B &) const = default;
};
constexpr bool f() {
B x;
return x == x;
}
static_assert( f() ); // fails in Clang
然后程序在 GCC 中成功,但在 Clang 中失败。在线演示:https://gcc.godbolt.org/z/5hYob3K66
两个程序的结构是否良好并且
f()
必须评估为 true
?
这只是 GCC/Clang 中的一个错误。
指定默认的相等运算符,以按声明顺序([class.eq]/3)对其操作数的基类子对象执行成员相等比较,后跟非静态数据成员。
比较是通过
a == b
执行的,其中 a
和 b
是表示操作数相应子对象的左值。 [class.compare.default]/5 明确表示这样的左值是
由一系列派生到基数的转换、类成员访问表达式和应用于
的数组下标表达式组成x
其中
x
是默认相等运算符的相应参数,它必然是 const
限定的。
因此,遵循语言的正常规则,
a
和b
也应该继承这个const
限定(引用mutable
数据成员时除外),但由于某种原因,这在GCC中不会发生非静态数据成员,而 Clang 巧合地在基类子对象上表现出相同的错误行为。
在OP示例中,此错误导致选择
A::operator int&()
而不是A::operator int() const
来执行内置A
的int
->operator==(int, int)
转换。