内置运算符 == 重载的解析

Resolution of built-in operator == overloads

在下面的代码中,struct A 有两个到 charint 的隐式转换运算符,并且比较结构的一个实例与整数常量 2 的相等性:

struct A {
    constexpr operator char() { return 1; }
    constexpr operator int() { return 2; }
};
static_assert( A{} == 2 );

代码在 GCC 和 MSVC 中通过良好,但 Clang 抱怨:

<source>:5:20: error: use of overloaded operator '==' is ambiguous (with operand types 'A' and 'int')
static_assert( A{} == 2 );
               ~~~ ^  ~
<source>:5:20: note: because of ambiguity in conversion of 'A' to 'float'
<source>:2:15: note: candidate function
    constexpr operator char() { return 1; }
              ^
<source>:3:15: note: candidate function
    constexpr operator int() { return 2; }
              ^
<source>:5:20: note: built-in candidate operator==(float, int)
static_assert( A{} == 2 );
                   ^
<source>:5:20: note: built-in candidate operator==(double, int)
<source>:5:20: note: built-in candidate operator==(long double, int)
<source>:5:20: note: built-in candidate operator==(__float128, int)
<source>:5:20: note: built-in candidate operator==(int, int)
...

演示:https://gcc.godbolt.org/z/h9Kd66heM

一般的问题是这里是哪个编译器?特别有趣的是,知道为什么 Clang 不喜欢 operator==(int, int),而是将它列在其他人中?

这是CWG 507。给了一个和你类似的例子,提交者解释说按照标准,重载解析是有歧义的,尽管这个结果很counter-intuitive.

转换为您的特定示例,在比较 operator==(int, int)operator==(float, int) 以确定哪个是更好的候选者时,我们必须确定哪个对第一个参数具有更好的隐式转换序列(显然在第二个参数中,不需要转换)。对于 operator==(int, int) 的第一个参数,我们只使用 A::operator int。对于operator==(float, int)的第一个参数,没有办法决定是使用A::operator int还是A::operator char,所以我们得到了“歧义转换序列”。重载决议规则表明,不明确的转换序列并不比任何其他 user-defined 转换序列更好或更差。因此,从 A{}int 的直接转换(通过 A::operator int)并不被认为比从 A{}float 的模糊转换更好。这意味着 operator== 候选人都不比另一个更好。

Clang 显然遵循标准的字面意思,而 GCC 和 MSVC 可能正在做其他事情,因为标准似乎在这里被打破了。 “哪个编译器是正确的”取决于您对标准应该的看法。问题页面上没有建议的解决方案。

我建议删除 operator char 除非你真的、真的需要它,在这种情况下你将不得不考虑你还愿意放弃什么。