以下代码
#include <string>
struct Foo {
operator double() {
return 1;
}
int operator[](std::string x) {
return 1;
}
};
int main() {
Foo()["abcd"];
}
Run Code Online (Sandbox Code Playgroud)
使用g ++进行编译很好,但由于声明的方法和本机运算符之间存在歧义,因此使用clang和intel编译器会失败[].
如果Foo有一个隐式转换int,我会很清楚,但转换是这样的double.这不是解决歧义吗?
§13.3.3.1.2[over.ics.user]/p1-2:
用户定义的转换序列包括初始标准转换序列,然后是用户定义的转换(12.3),后跟第二个标准转换序列.如果用户定义的转换由构造函数(12.3.1)指定,则初始标准转换序列将源类型转换为构造函数的参数所需的类型.如果转换函数(12.3.2)指定了用户定义的转换,则初始标准转换序列会将源类型转换为转换函数的隐式对象参数.
第二个标准转换序列将用户定义转换的结果转换为序列的目标类型.
特别是,存在从浮点到整数类型的隐式转换(§4.9[conv.fpint]/p1):
可以将浮点类型的prvalue转换为整数类型的prvalue.转换截断; 也就是说,丢弃小数部分.如果截断的值无法在目标类型中表示,则行为未定义.
出于重载决策的目的,适用的候选人是:
Foo::operator[](std::string x) // overload
operator[](std::ptrdiff_t, const char *); // built-in
Run Code Online (Sandbox Code Playgroud)
给出一个类型的参数列表(Foo, const char [5]).
要匹配第一个运算符函数,第一个参数是完全匹配; 第二个需要用户定义的转换.
要匹配第二个内置函数,第一个参数需要用户定义的转换序列(用户定义的转换double后跟标准转换std::ptrdiff_t,浮动积分转换).第二个参数需要标准的数组到指针转换(仍然是完全匹配等级),这比用户定义的转换要好.
因此,对于第一个参数,第一个函数更好; 对于第二个参数,第二个函数更好,我们有一个纵横交错的情况,重载解析失败,程序格式不正确.
注意,虽然为了操作符重载解析的目的,用户定义的转换序列可以有两个标准转换序列(一个在用户定义的转换之前和一个之后),并且非类型的操作数可以转换为匹配候选者,如果选择了内置运算符,则第二个标准转换序列不适用于类类型的操作数,并且在将运算符解释为内置类之前,根本不对非类类型的操作数应用转换(§13.3.1.2[over.match.oper]/p7):
如果通过重载决策选择内置候选,则类类型的操作数将转换为所选操作函数的相应参数的类型,但用户定义的转换序列的第二个标准转换序列除外(13.3.3.1 .2)不适用.然后将操作符视为相应的内置操作符,并根据第5章进行解释.
因此,如果Foo::operator[](std::string x)删除,编译器应报告错误,但clang不会.这是一个明显的铿锵声,因为它无法拒绝标准中给出的示例.
| 归档时间: |
|
| 查看次数: |
840 次 |
| 最近记录: |