为什么以下重载函数调用不明确?编译错误:
调用重载'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;
}
答案 0 :(得分:6)
您遇到错误,因为重载解析必须从两个同样可行的函数中选择(两者都具有用户定义的转换)。函数重载决策是一个非常复杂的主题。有关重载分辨率的更多详细信息,请参阅这是Stephan T. Lavavej的recent lecture。通常最好创建单参数构造函数explicit
,然后使用显式构造函数参数调用函数。
test(0L)
与任何重载都不完全匹配,因为没有重载test(long)
。您提供的两个重载都在其参数上具有用户定义的转换,但编译器认为它们同样可行。 A
重载必须进行标准转换(long到int),然后是用户定义的转换(int到A),B
重载用户定义的转换(长到B)。但两者都是隐式的用户定义转换序列。
这些排名如何?标准在 13.3.3.2中对隐式转换序列进行排名[over.ics.rank]
标准转换序列S1是比转换序列更好的转换序列 标准转换序列S2,如果S1是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 :(得分:1)
函数重载考虑精确的参数类型或隐式转换。 在您的示例中,从重载的角度来看,备选方案A(0L)和B(0L)都是相同的,因为需要隐式构造函数调用。
答案 2 :(得分:1)
您正在使用long类型的参数调用test。
没有测试(长)。
编译器必须在测试(A)和测试(B)之间进行选择。
要调用测试(A),它的转换序列为long - > int - >甲
为了调用测试(B),它具有长的转换序列 - >乙
根据标准的排名规则,如果一个排名比另一个排名更好,它将选择一个 - 或者它会因模糊而失败。
在这种特定情况下,两个转换序列的排名相同。
标准中有很多关于如何计算转换序列排名的规则 13.3.3最佳可行功能
答案 3 :(得分:0)
试试这个:
class A
{
public:
explicit A(int){}
A(){}
};
关键字显式停止编译器进行隐式转换。
答案 4 :(得分:0)
允许编译器只对用户类型进行一次隐式转换。如果这也涉及原始类型之间的转换,则它们不计算在内。即使您在test(B)
的情况下有两个转换,但以下内容无法编译:
class B
{
public:
B(int) {}
};
class A
{
public:
A(const B&) {}
};
void test(const A&) {}
....
test(5);
要禁用编译器进行隐式转换,您应该使用explicit
关键字和构造函数