I tried to understand the ambiguous calls in below code, but i am not able to get it. Three of them give an error.
struct A {
virtual void func(int a, float b) const { std::cout << "a1" << std::endl; }
virtual void func(double a, float b) { std::cout << "a2" << std::endl; }
void func(unsigned int a, char b) { std::cout << "a3" << std::endl; }
};
int main() {
A baseObj;
baseObj.func(1, 1.0);//Error : expecting a1
baseObj.func(2, 2.0f);//Error : expecting a1
baseObj.func(3, 'c');//Error : expecting a3
return 0;
}
if I change it to:
baseObj.func(1.0, 1.0);//a2
baseObj.func(2.0, 2.0f);//a2
baseObj.func(3, 'c');//Error, if i remove unsigned from function, then it gives a3.
// I dont know what exactly happening here
Can someone explain how to figure out the ambiguous call?
thanks
As someone has pointed it out in the now deleted answer, MSVC has the most eloquent explanation:
<source>(13): error C2666: 'A::func': overloaded functions have similar conversions
<source>(7): note: could be 'void A::func(unsigned int,char)'
<source>(6): note: or 'void A::func(double,float)'
<source>(5): note: or 'void A::func(int,float) const'
<source>(13): note: while trying to match the argument list '(int, double)'
<source>(14): error C2666: 'A::func': overloaded functions have similar conversions
<source>(7): note: could be 'void A::func(unsigned int,char)'
<source>(6): note: or 'void A::func(double,float)'
<source>(5): note: or 'void A::func(int,float) const'
<source>(14): note: while trying to match the argument list '(int, float)'
<source>(14): note: note: qualification adjustment (const/volatile) may be causing the ambiguity
<source>(15): error C2666: 'A::func': overloaded functions have similar conversions
<source>(7): note: could be 'void A::func(unsigned int,char)'
<source>(6): note: or 'void A::func(double,float)'
<source>(5): note: or 'void A::func(int,float) const'
<source>(15): note: while trying to match the argument list '(int, char)'
You can see it for yourself here: https://godbolt.org/z/ee4E61d79
Now, why is that?
In the first call you are passing an int
(1
) and a double
(1.0
) and expecting a1
to be called. However, a1
requires a const object (due to the const
qualifier), an int
and a float
.
So, there are two conversions that have to happen: baseObj
has to be "converted" from A
to const A
and 1.0
has to be converted from double
to float
. Likewise, a2
requires 2 conversions -- 1
to double
and 1.0
to float
. Same with a3
-- 1
to unsigned int
and 1.0
to char
.
In the eyes of the compiler all 3 candidates are equivalent, as they all require conversions. So, it doesn't know which one it should call and gives an error.
Similar things happen in the second and third calls.
UPDATE in response to the comments.
Overload ranking is a fairly complicated process covered in over.ics.rank. In an attempt to keep my answer simple -- and in the hopes that no one will notice ;), I've oversimplified things a bit.
But, without spending hours digging through the standard, this is what's happening as far as I can tell:
Section 4 says that conversion sequences are ordered by their rank. In the 3rd call all 3 candidates have the same rank -- Conversion, because they all require a conversion.
NB: int
-> unsigned int
is a conversion and not a promotion.
Section 3.2 defines when one conversion sequence is considered better than the other. And sequences of the same rank are considered indistinguishable (neither is better) regardless of the number of conversions.
So, that's why the 3rd (as well as the first two) calls fail.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With