以下代码
#include <string>
struct Foo {
operator double() {
return 1;
}
int operator[](std::string x) {
return 1;
}
};
int main() {
Foo()["abcd"];
}
使用g ++进行编译,但由于声明的方法与本机运算符[]
之间存在歧义,因此无法使用clang和intel编译器。
如果Foo
隐式转换为int
,我会很清楚,但此处转换为double
。是不是解决了歧义?
答案 0 :(得分:7)
§13.3.3.1.2[over.ics.user] / p1-2:
用户定义的转换序列由初始标准组成 转换序列后跟用户定义的转换(12.3) 然后是第二个标准转换序列。如果是用户定义的 转换由构造函数(12.3.1)指定,即初始值 标准转换序列将源类型转换为类型 构造函数的参数所需要的。如果是用户定义的 转换由转换函数(12.3.2)指定,即初始值 标准转换序列将源类型转换为隐式 转换函数的对象参数。
第二个标准转换序列转换结果 用户定义的转换为序列的目标类型。
特别是,从浮点到整数类型的隐式转换(§4.9[conv.fpint] / p1):
浮点类型的prvalue可以转换为a的prvalue 整数类型。转换截断;也就是说,小数部分 被丢弃了。如果截断值不能,则行为未定义 用目的地类型表示。
出于重载决策的目的,适用的候选人是:
Foo::operator[](std::string x) // overload
operator[](std::ptrdiff_t, const char *); // built-in
给出类型(Foo, const char [5])
的参数列表。
要匹配第一个运算符函数,第一个参数是完全匹配;第二个需要用户定义的转换。
要匹配第二个内置函数,第一个参数需要用户定义的转换序列(用户定义的转换为double
,然后标准转换为std::ptrdiff_t
,浮点积分转换)。第二个参数需要标准的数组到指针转换(仍然是完全匹配等级),这比用户定义的转换要好。
因此对于第一个参数,第一个函数更好;对于第二个参数,第二个函数更好,我们有一个纵横交错的情况,重载解析失败,程序格式不正确。
注意,虽然为了操作符重载解析的目的,用户定义的转换序列可以有两个标准转换序列(一个在用户定义的转换之前和之后),非类型的操作数可以是转换为匹配候选者,如果选择了内置运算符,则第二个标准转换序列不应用于类类型的操作数,并且在将运算符解释为a类之前,根本不对非类类型的操作数应用转换。内置(§13.3.1.2[over.match.oper] / p7):
如果通过重载分辨率选择内置候选,则 类类型的操作数转换为相应的类型 所选操作功能的参数,除了第二个 用户定义的转换序列的标准转换序列 (13.3.3.1.2)不适用。然后操作员被视为 相应的内置运算符,并根据第5条进行解释。
因此,如果删除Foo::operator[](std::string x)
,编译器应该报告错误,尽管clang没有。这是一个明显的铿锵声,如it fails to reject the example given in the standard。