具有多个函数和多个转换运算符的重载解析
Overload resolution with multiple functions and multiple conversion operators
考虑简单的代码:
#include<iostream>
struct A {
operator double(){
std::cout<<"Conversion function double chosen."<<std::endl;
return 1.1;
}
operator char(){
std::cout<<"Conversion function char chosen."<<std::endl;
return 'a';
}
} a;
void foo(int){}
void foo (char){}
int main() {
foo(a);
}
以上代码工作正常,正如预期的那样 gcc、clang 和 VC++ 选择 foo(char)
。
现在让我们稍微修改一下代码:
#include<iostream>
struct A {
operator double(){
std::cout<<"Conversion function double chosen."<<std::endl;
return 1.1;
}
operator char(){
std::cout<<"Conversion function char chosen."<<std::endl;
return 'a';
}
} a;
void foo(int){}
void foo (double){} //parameter changed from char to double
int main() {
foo(a);
}
现在应该选择 foo(double)
,但似乎只有 VC++ 对代码感到满意,而 clang 和 gcc 对上面的代码不满意。
main.cpp:11:10: error: call of overloaded 'foo(A&)' is ambiguous
foo(a);
^
main.cpp:8:6: note: candidate: void foo(int)
void foo(int){}
^
main.cpp:9:6: note: candidate: void foo(double)
void foo (double){} //parameter changed from char to double
^
谁能解释为什么上面的代码失败了?还是bug?
还有一个问题:gcc和clang是否共享重载解析的代码?
A -> char
是 A -> char
.
A -> int
是 A -> char -> int
(因为 char
到 int
是一种提升,因此优于 double
到 int
的转换)。
A -> double
是 A -> double
.
两个用户自定义转换序列只有涉及相同的用户自定义转换函数时才具有可比性。因此,A -> char
是比 A -> int
更好的转换序列,因此您的第一种情况是明确的。 A -> int
和 A -> double
都不比另一个好,所以第二种情况是模棱两可的。
TL;DR:不同之处在于,在第一种情况下,相对于第二种情况,用户定义的转换序列(A -> char
、A -> int
) 调用相同的转换函数 (operator char
)。这使我们能够通过 [over.ics.rank]/(3.3).
打破平局
特定函数的最佳转换运算符由 [over.match.best]/(1.4) 选择(比较其 return 类型的转换序列)。
因此,foo(int)
的更好转换函数是 operator char
,然后提升为 int
,而不是 operator double
,然后是浮点转换。
现在考虑第二个重载的两种变体:
foo(char)
的最佳 ICS 是 也通过 operator char
(身份比浮点转换更好)。因此 [over.ics.rank]/(3.3) 适用:
User-defined conversion sequence U1
is a better conversion sequence than another user-defined conversion sequence U2
if they contain the same user-defined conversion function […] and in either case the second standard conversion sequence of U1
is better than the second standard conversion sequence of U2
.
因此整体转换为F2
被认为更好,因此被选中。
到 foo(double)
的最佳 ICS 是 通过 operator double
。我们最终得到两个使用不同转换函数的转换序列;没有什么真正适用,我们只是模棱两可。
考虑简单的代码:
#include<iostream>
struct A {
operator double(){
std::cout<<"Conversion function double chosen."<<std::endl;
return 1.1;
}
operator char(){
std::cout<<"Conversion function char chosen."<<std::endl;
return 'a';
}
} a;
void foo(int){}
void foo (char){}
int main() {
foo(a);
}
以上代码工作正常,正如预期的那样 gcc、clang 和 VC++ 选择 foo(char)
。
现在让我们稍微修改一下代码:
#include<iostream>
struct A {
operator double(){
std::cout<<"Conversion function double chosen."<<std::endl;
return 1.1;
}
operator char(){
std::cout<<"Conversion function char chosen."<<std::endl;
return 'a';
}
} a;
void foo(int){}
void foo (double){} //parameter changed from char to double
int main() {
foo(a);
}
现在应该选择 foo(double)
,但似乎只有 VC++ 对代码感到满意,而 clang 和 gcc 对上面的代码不满意。
main.cpp:11:10: error: call of overloaded 'foo(A&)' is ambiguous
foo(a);
^
main.cpp:8:6: note: candidate: void foo(int)
void foo(int){}
^
main.cpp:9:6: note: candidate: void foo(double)
void foo (double){} //parameter changed from char to double
^
谁能解释为什么上面的代码失败了?还是bug?
还有一个问题:gcc和clang是否共享重载解析的代码?
A -> char
是 A -> char
.
A -> int
是 A -> char -> int
(因为 char
到 int
是一种提升,因此优于 double
到 int
的转换)。
A -> double
是 A -> double
.
两个用户自定义转换序列只有涉及相同的用户自定义转换函数时才具有可比性。因此,A -> char
是比 A -> int
更好的转换序列,因此您的第一种情况是明确的。 A -> int
和 A -> double
都不比另一个好,所以第二种情况是模棱两可的。
TL;DR:不同之处在于,在第一种情况下,相对于第二种情况,用户定义的转换序列(A -> char
、A -> int
) 调用相同的转换函数 (operator char
)。这使我们能够通过 [over.ics.rank]/(3.3).
特定函数的最佳转换运算符由 [over.match.best]/(1.4) 选择(比较其 return 类型的转换序列)。
因此,foo(int)
的更好转换函数是 operator char
,然后提升为 int
,而不是 operator double
,然后是浮点转换。
现在考虑第二个重载的两种变体:
foo(char)
的最佳 ICS 是 也通过operator char
(身份比浮点转换更好)。因此 [over.ics.rank]/(3.3) 适用:User-defined conversion sequence
U1
is a better conversion sequence than another user-defined conversion sequenceU2
if they contain the same user-defined conversion function […] and in either case the second standard conversion sequence ofU1
is better than the second standard conversion sequence ofU2
.因此整体转换为
F2
被认为更好,因此被选中。到
foo(double)
的最佳 ICS 是 通过operator double
。我们最终得到两个使用不同转换函数的转换序列;没有什么真正适用,我们只是模棱两可。