当您拥有x + i 时,由于x 属于类类型,因此重载决议开始:
标准中的具体细节([over.match.oper]p2)
如果任一操作数的类型是类或枚举,则可能会声明实现此运算符的用户定义的运算符函数,或者可能需要用户定义的转换将操作数转换为适合的类型内置运算符。
在这种情况下,重载决策用于确定要调用哪个运算符函数或内置运算符来实现运算符。
内置候选项在第 3.3 段中定义:
对于operator ,、一元operator &或operator ->,内置候选集为空。对于所有其他运算符,内置候选函数包括 [over.built] 中定义的所有候选运算符函数,与给定运算符相比,
- 具有相同的操作员名称,并且
- 接受相同数量的操作数,并且
- 接受可以根据 [over.best.ics] 将给定操作数转换为的操作数类型,并且
- 不具有与任何非函数模板特化的非成员候选相同的参数类型列表。
根据 [over.built]p13,内置候选函数可能包括:
对于每一对类型 L 和 R,其中 L 和 R 都是浮点或提升的整数类型,存在形式为的候选运算符函数
LR operator*(L, R);
...
LR operator+(L, R);
...
bool operator>=(L, R);
其中 LR 是类型 L 和 R 之间通常算术转换 ([expr.arith.conv]) 的结果。
所以有一个内置函数int operator+(int, int)。
至于有哪些可能的隐式转换序列:
[over.best.ics]p3:
格式良好的隐式转换序列是以下形式之一:
- 标准转换序列,
- 用户定义的转换序列,或
- 省略号转换序列。
这里使用了用户定义的转换序列,由[over.ics.user]定义:
用户定义的转换序列由初始标准转换序列和用户定义的转换 ([class.conv]) 以及第二个标准转换序列组成。
(这里,两个标准转换序列都是空的,可以使用用户定义的转换为int)
因此,当检查 int operator+(int, int) 是否是内置候选对象时,这是因为您的类类型和 int 之间存在转换。
至于实际的重载解析,来自[over.match.oper]:
- 用于某些运算符 @ 的重载解决方案的候选函数集是成员候选函数、非成员候选函数、内置候选函数以及该运算符 @ 的重写候选函数的并集。
- 参数列表包含运算符的所有操作数。
根据[over.match.viable]和[over.match.best]从候选函数集合中选出最佳函数。
int operator+(int, int) 显然是最好的匹配,因为它不需要对第二个参数进行转换,而只需要对第一个参数进行用户定义的转换,因此它击败了其他候选者,例如 long operator+(long, int) 和 long operator+(int, long)
您可以看到内置候选集为空的问题,因为 GCC 错误报告有 no 个可行的候选集。如果你有:
auto add(int a, int b) -> int
{
return a + b;
}
auto main() -> int
{
auto i = int{0};
auto x = Wrapper<int>(i);
return add(x, i);
}
它现在可以使用 GCC 正常编译,因为 ::add(int, int) 被认为是候选者,即使它与内置运算符 int operator+(int, int) 应该没有什么不同。
如果你有:
template<class S = T>
operator S() { return _value; } // Can convert to any type
Clang 现在有错误:
<source>:16:14: error: use of overloaded operator '+' is ambiguous (with operand types 'Wrapper<int>' and 'int')
return x + i;
~ ^ ~
<source>:16:14: note: built-in candidate operator+(float, int)
<source>:16:14: note: built-in candidate operator+(double, int)
<source>:16:14: note: built-in candidate operator+(long double, int)
<source>:16:14: note: built-in candidate operator+(__float128, int)
<source>:16:14: note: built-in candidate operator+(int, int)
<source>:16:14: note: built-in candidate operator+(long, int)
<source>:16:14: note: built-in candidate operator+(long long, int)
<source>:16:14: note: built-in candidate operator+(__int128, int)
<source>:16:14: note: built-in candidate operator+(unsigned int, int)
<source>:16:14: note: built-in candidate operator+(unsigned long, int)
<source>:16:14: note: built-in candidate operator+(unsigned long long, int)
<source>:16:14: note: built-in candidate operator+(unsigned __int128, int)
(请注意,此错误消息不包括第二个参数的转换,但由于永远不会选择这些,因此它们可能不被视为优化)
而且 GCC 仍然说根本没有候选者,尽管所有这些内置候选者都存在。