这个例子似乎用VC10和gcc编译(虽然我的gcc版本很老) .
编辑:R . Martinho Fernandez在gcc 4.7上试过这个并且行为仍然是一样的 .
struct Base
{
operator double() const { return 0.0; }
};
struct foo
{
foo(const char* c) {}
};
struct Something : public Base
{
void operator[](const foo& f) {}
};
int main()
{
Something d;
d["32"];
return 0;
}
但克朗抱怨道:
test4.cpp:19:6: error: use of overloaded operator '[]' is ambiguous (with operand types 'Something' and 'const char [3]')
d["32"]
~^~~~~
test4.cpp:13:10: note: candidate function
void operator[](const foo& f) {}
^
test4.cpp:19:6: note: built-in candidate operator[](long, const char *)
d["32"]
^
test4.cpp:19:6: note: built-in candidate operator[](long, const restrict char *)
test4.cpp:19:6: note: built-in candidate operator[](long, const volatile char *)
test4.cpp:19:6: note: built-in candidate operator[](long, const volatile restrict char *)
重载决议考虑了这个表达式,考虑了两个可能的函数:
-
调用Something :: operator [](在用户定义的转换之后)
-
为const char *调用内置运算符(想想"32" [d])(在用户定义转换和标准转换后加倍到long) .
如果我写了 d["32"]
为 d.operator[]("32")
,那么重载解析甚至不会查看选项2,而clang也可以正常编译 .
编辑:(澄清问题)
这似乎是重载决策中的一个复杂区域,因此我非常感谢在这种情况下详细解释过载分辨率的答案,并引用标准(如果有一些模糊/高级可能是未知规则) .
如果铿锵是正确的,我也有兴趣知道为什么这两个是模棱两可的/一个不优于另一个 . 答案可能需要解释重载决策如何考虑两个候选者所涉及的隐式转换(用户定义和标准转换)以及为什么一个不比另一个好 .
注意:如果operator double()更改为operator bool(),则所有三个(clang,vc,gcc)将拒绝编译时出现类似的模糊错误 .
3 回答
通过逐步完成重载分辨率,应该更容易理解为什么重载决策是模糊的 .
§13.5.5 [over.sub]
现在,我们首先需要一个重载集 . 这是根据
§13.3.1
构建的,包含成员以及非成员函数 . 有关更详细的说明,请参见this answer of mine .§13.3.1 [over.match.funcs]
然后,构造一个参数列表:
然后针对重载集的每个成员测试参数列表:
然后,由于我们发现需要隐式转换,我们来看看
§13.3.3 [over.match.best] p1
:现在让我们在重载集(
§13.3.3.1
)中构造f1
和f2
的隐式转换序列:§13.3.3.2 [over.ics.rank] p2
因此
ICS1(f1)
优于ICS1(f2)
且ICS2(f1)
比ICS2(f2)
更差 .相反,
ICS1(f2)
比ICS1(f1)
差,ICS2(f2)
优于ICS2(f1)
.§13.3.3 [over.match.best]
好吧,f * ck . :)因此,Clang拒绝该代码是正确的 .
似乎毫无疑问,
Something::operator[](const foo& f)
和内置的operator[](long, const char *)
都是可行的候选函数(13.3.2),用于重载决策 . 真实参数的类型是Something
和const char*
,以及我认为的隐式转换序列(ICF):for
Something::operator[](const foo& f)
:(1-1)身份转换,(1-2)foo("32")
至foo::foo(const char*)
;for
operator[](long, const char *)
:(2-1)long(double(d))
至Something::operator double() const
(继承自Base),以及(2-2)身份转换 .现在,如果我们根据(13.3.3.2)对这些ICF进行排名,我们可以看出(1-1)是比(2-1)更好的转换,而(1-2)是比(2-2)更差的转换 . 根据(13.3.3)的定义,
因此,所考虑的两个候选函数都不比另一个好,因此呼叫是不正确的 . 即Clang似乎是正确的,代码不应该编译 .
从C11规范中的13.6可以看出,clang在这里是正确的:
edit
一旦你了解了哪些运算符函数存在,这只是标准的重载分辨率,如标准的第13.3节所述 - 大约10页的细节,但它的要点是函数调用不是模糊的,需要是一个单独的函数,至少与每个参数上所有可能的,可行的函数一样好,并且在至少一个参数上比其他函数更好地匹配 . 关于“更好”意味着什么,有很多规范细节,但它归结为(在这种情况下)不需要任何用户定义的转换运算符或对象构造函数的匹配比具有更好的匹配 .
所以在这种情况下,有两个可行的匹配:
第一个是第一个参数的更好匹配,而第二个是第二个参数的更好匹配 . 因此,除非有一些其他功能比这两个功能更好,否则其含糊不清 .
后一点是一个可能的解决方法 - 添加:
上课的东西