为啥这些重载的函数调用模棱两可?
Posted
技术标签:
【中文标题】为啥这些重载的函数调用模棱两可?【英文标题】:Why are these overloaded function calls ambiguous?为什么这些重载的函数调用模棱两可? 【发布时间】:2012-08-06 12:16:31 【问题描述】:为什么下面的重载函数调用不明确?出现编译错误:
重载'test(long int)'的调用不明确,候选者是:void test(A)| 无效测试(B)|
代码:
class A
public:
A(int)
A()
;
class B: public A
public:
B(long)
B()
;
void test(A a)
void test(B b)
void main()
test(0L);
return;
【问题讨论】:
【参考方案1】:您遇到了一个错误,因为重载解析必须从两个同样可行的函数中进行选择(两者都有用户定义的转换)。函数重载解析是一个非常复杂的主题。有关重载解决方案的更多详细信息,请参见例如这个recent lecture 由 Stephan T. Lavavej 提供。通常最好创建单参数构造函数explicit
,然后使用显式构造函数参数调用您的函数。
test(0L)
与任何重载都不完全匹配,因为没有重载 test(long)
。您提供的两个重载都对其参数进行了用户定义的转换,但编译器认为它们同样可行。 A
重载必须执行标准转换(long 到 int),然后是用户定义的转换(int 到 A),B
重载用户定义的转换(long 到 B)。但两者都是用户定义的隐式转换序列。
这些排名如何?该标准在13.3.3.2 Ranking implicit conversion sequences [over.ics.rank]
中说标准转换序列 S1 比转换序列更好 如果 S1 是 S2 的适当子序列,则标准转换序列 S2
这些类型的平局,例如如果 A 是 B 的派生类,则适用(反之亦然)。但是这里没有一个转换序列是另一个的子序列。因此它们同样可行,编译器无法解析调用。
class A
public:
explicit A(int)
A()
;
class B: public A
public:
explicit B(long)
B()
;
void test(A a)
void test(B b)
int main()
test(A(0L)); // call first overload
test(B(0L)); // call second overload
return 0;
注意:这是int main()
,而不是void main()
。
【讨论】:
+1,这是最正式的正确答案,尽管您可以稍微扩展一下解释。 @rhalbersma:但我认为 test(0L) 与 test(B b) 更精确匹配?为什么会模棱两可?0L
是long
,所以你的第二段应该说“没有test(long)
”。
@huwang 查看更新的答案:只有当 long->B 是 long->int->A 的子序列,反之亦然,B
重载才会更接近。
【参考方案2】:
函数重载会考虑精确的参数类型或隐式转换。 在您的示例中,从重载的角度来看,替代 A(0L) 和 B(0L) 都是相同的,因为需要隐式构造函数调用。
【讨论】:
【参考方案3】:您正在使用 long 类型的参数调用 test。
没有测试(长)。
编译器必须在 test(A) 和 test(B) 之间进行选择。
调用 test(A) 它有一个 long -> int -> A 的转换序列。
调用 test(B) 它有一个 long -> B 的转换序列。
根据标准的排名规则,如果一个排名比另一个好,它会选择一个 - 或者它会因模棱两可而失败。
在这种特定情况下,两个转换序列的排名相同。
在13.3.3“最佳可行函数”部分中,标准中有一长串关于如何计算转换序列排名的规则
【讨论】:
在标准中。我会帮你查的,等一下。 但它没有解释为什么不搜索最接近的潜在转化树。 我已经更新了我的答案,其实我说的太简单了。有一种方法可以对它们进行排名。在这种情况下,两个转换序列的排名是相同的。【参考方案4】:试试这个:
class A
public:
explicit A(int)
A()
;
关键字显式停止编译器进行隐式转换。
【讨论】:
【参考方案5】:编译器只允许对用户类型进行一次隐式转换。如果这还涉及原始类型之间的转换,则它们不计算在内。即使您在 test(B)
的情况下进行了两次转换,但以下内容将无法编译:
class B
public:
B(int)
;
class A
public:
A(const B&)
;
void test(const A&)
....
test(5);
要禁用编译器进行隐式转换,您应该使用 explicit
关键字和构造函数
【讨论】:
以上是关于为啥这些重载的函数调用模棱两可?的主要内容,如果未能解决你的问题,请参考以下文章