C++ 这个应该编译吗?重载解析和隐式转换
这个例子似乎是用VC10和gcc编译的(尽管我的gcc版本很旧) 编辑:R.Martinho Fernandez在GCC4.7上尝试过这个,但行为仍然是一样的C++ 这个应该编译吗?重载解析和隐式转换,c++,visual-c++,clang,implicit-conversion,overload-resolution,C++,Visual C++,Clang,Implicit Conversion,Overload Resolution,这个例子似乎是用VC10和gcc编译的(尽管我的gcc版本很旧) 编辑:R.Martinho Fernandez在GCC4.7上尝试过这个,但行为仍然是一样的 struct Base { operator double() const { return 0.0; } }; struct foo { foo(const char* c) {} }; struct Something : public Base { void operator[](const foo&am
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;
}
但clang抱怨说:
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 *)
重载解析考虑了两个可能的函数:
- 调用某物::运算符[](在用户定义的转换之后)
- 调用const char*的内置运算符(考虑“32”[d])(在用户定义的转换和标准转换双到长之后)
d[“32”]
编写为d.operator[](“32”)
,那么重载解析甚至不会查看选项2,而且clang也可以很好地编译
编辑:(澄清问题)
这似乎是过载解决方案中的一个复杂领域,因此我非常感谢详细解释本例过载解决方案的答案,并引用标准(如果有一些模糊/高级的可能未知的规则)
如果clang是正确的,我还想知道为什么这两个词不明确/一个不比另一个好。答案可能必须解释重载解析如何考虑两个候选对象上涉及的隐式转换(用户定义转换和标准转换),以及为什么一个不比另一个好
注意:如果运算符double()更改为运算符bool(),所有三个(clang、vc、gcc)都将拒绝编译,并出现类似的模糊错误。从C++11规范的13.6中可以看出,clang在这里是正确的: 13.6 内置运算符 [over.build] 中指定了代表第5条中定义的内置运算符的候选运算符函数 本款。如前所述,这些候选函数参与操作员过载解决过程 在13.3.1.2中,且不得用于其他目的。[注意:因为内置运算符只接受带 非类类型,并且仅当操作数表达式最初具有类时才会发生运算符重载解析 或枚举类型,运算符重载解析只能在操作数 具有用户定义的转换为适合于运算符的非类类型的类类型,或者 操作数的枚举类型可以转换为适合运算符的类型。另请注意 本款中给出的某些候选运算符函数比内置运算符函数更为宽松 操作员自身。如13.3.1.2所述,通过过载分辨率选择内置操作员后 该表达式受第5条中给出的内置运算符要求的约束,因此 此处给出的任何附加语义约束。如果存在同名的用户编写的候选项 和参数类型作为内置的候选运算符函数,内置运算符函数是隐藏的 不包括在候选函数集中。-结束注释] : 对于每个cv限定或cv非限定对象类型T,都存在表单的候选运算符函数 T&operator[](T*,std::ptrdiff\U T) T&operator[](std::ptrdiff_T,T*) 编辑 一旦你通过了哪些操作符函数的存在,这就变成了标准的重载解决方案,如标准第13.3节所述——大约10页的细节,但其要点是,为了使函数调用不含糊,需要有一个至少尽可能匹配的单一函数,每个参数上都有可行函数,并且至少在一个参数上比其他参数更匹配。关于“更好”的确切含义有很多规范细节,但归结起来(在本例中)不需要任何用户定义的转换运算符或对象构造函数的匹配比需要的匹配要好 因此,在这种情况下,有两种可行的匹配:
void Something::operator[](const foo& f)
operator[](long, const char *)
第一个参数与第一个参数更匹配,而第二个参数与第二个参数更匹配。因此,除非有其他函数比这两个函数都好,否则它是不明确的
后一点是一个可能的解决方法——添加:
要对某个对象进行分类,似乎毫无疑问,
Something::operator[](const foo&f)
和内置的operator[](long,const char*)
都是解决重载问题的可行候选函数(13.3.2)。实数参数的类型是Something
和const char*
,我认为隐式转换序列(ICF)是:
- 对于
:(1-1)身份转换,以及(1-2)Something::operator[](const-foo&f)
通过foo(“32”)
李>foo::foo(const-char*)
- 对于
:(2-1)运算符[](long,const char*)
通过long(double(d))
(从基继承)和(2-2)身份转换Something::operator double()const
因此,所考虑的两个候选函数都不比另一个好,因此调用是病态的。例如,叮当声似乎是正确的,代码不应编译。通过一步一步地检查,应该更容易理解为什么重载分辨率不明确
§13.5.5[over.sub]
因此,下标表达式x[y]
被解释为类的x.operator[](y)
void operator[](const char *a) { return (*this)[foo(a)]; }
// abstract overload set (return types omitted since irrelevant)
f1(Something&, foo const&); // linked to Something::operator[](foo const&)
f2(std::ptrdiff_t, char const*); // linked to operator[](std::ptrdiff_t, char const*)
f3(char const*, std::ptrdiff_t); // linked to operator[](char const*, std::ptrdiff_t)
// abstract argument list
(Something&, char const[3]) // 'Something&' is the implied object argument
f1 -> identity match on argument 1, conversion required for argument 2
f2 -> conversion required for argument 1, conversion required for argument 2 (decay)
f3 -> argument 1 incompatible, argument 2 incompatible, discarded
ICS1(f1): 'Something&' -> 'Someting&', standard conversion sequence
ICS2(f1): 'char const[3]' -> 'foo const&', user-defined conversion sequence
ICS1(f2): 'Something&' -> 'std::ptrdiff_t', user-defined conversion sequence
ICS2(f2): 'char const[3]' -> 'char const*', standard conversion sequence