C++ 运算符[](常量字符*)歧义

C++ 运算符[](常量字符*)歧义,c++,C++,下面的代码 #include <string> struct Foo { operator double() { return 1; } int operator[](std::string x) { return 1; } }; int main() { Foo()["abcd"]; } #包括 结构Foo{ 运算符双(){ 返回1; } int运算符[](标准::字符串x){ 返回1; } }; in

下面的代码

#include <string>

struct Foo {
    operator double() {
        return 1;
    }

    int operator[](std::string x) {
        return 1;
    }
};

int main() {
    Foo()["abcd"];
}
#包括
结构Foo{
运算符双(){
返回1;
}
int运算符[](标准::字符串x){
返回1;
}
};
int main(){
Foo()[“abcd”];
}
使用g++可以很好地编译,但使用clang和intel编译器会失败,因为声明的方法与本机运算符
[]
之间存在歧义


我很清楚,如果
Foo
有一个到
int
的隐式转换,但是这里的转换是到
double
。这难道不能解决歧义吗?

§13.3.3.1.2[超过ics.user]/p1-2:

用户定义的转换序列由初始标准组成 用户定义转换后的转换顺序(12.3) 然后是第二个标准转换序列。如果用户定义了 转换由构造函数(12.3.1)指定,初始 标准转换序列将源类型转换为 构造函数的参数所需。如果用户定义了 转换由转换函数(12.3.2)指定,初始 标准转换序列将源类型转换为隐式类型 转换函数的对象参数

第二个标准转换序列转换 用户定义的到序列目标类型的转换

特别是,存在从浮点到整数类型的隐式转换(§4.9[conv.fpint]/p1):

浮点类型的prvalue可以转换为 整数类型。转换截断;即小数部分 被丢弃。如果无法定义截断的值,则行为未定义 无法在目标类型中表示

为了解决过载问题,适用的候选方案包括:

Foo::operator[](std::string x)              // overload
operator[](std::ptrdiff_t, const char *);   // built-in
给定类型的参数列表
(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没有。这是一个明显的铿锵错误,因为。

如果将转换运算符删除为
double
,会发生什么情况?在没有转换运算符的情况下,我可以在铿锵上正常工作。这与在我的计算机上删除
Foo::operator[]
?@n.m.使用icl(icc的Windows版本)时使用icc编译的问题几乎相同,它确实可以编译。但是icl也错误地编译了§13.3.1.2[over.match.oper]/p7中标准规定不应该编译的一个例子,所以……但是clang拒绝将
1.0[“foo”]
视为无效。@n.m.这也是正确的-在这种情况下没有重载解决方案。此外,如果重载解析选择了内置的,结果就不一定有效。在我的机器上,clang抱怨
Foo::operator[](std::string)
和内置的
operator[](int,const char*)
(不涉及ptrdiff)之间存在歧义。当您删除
Foo::operator[]
时,它会愉快地选择一个没有错误的内置函数。但内置函数要求其中一个参数为整型。你怎么解释呢?@n.m.
std::ptrdiff\u t
是一个有符号整数类型的typedef(显然在你的机器上是
int
——我假设它是32位?)。第二个是叮当作响的虫子。它不应该转换非类类型的参数,也不应该为类类型的参数应用第二个标准转换序列,但它确实转换了。(1) 内置元素不可行,因此应从重载集中删除它,并选择
Foo::operator[]
作为唯一剩余的元素。因为clang错误地认为内置是可行的,所以这种情况不会发生。(2) 该标准意味着内置是可行的,并要求在过载解决阶段后将其拒绝。如果是这种情况,那就是标准中的缺陷(一种意外的、不合逻辑的行为,没有任何理由)。