Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Understanding the ambiguous calls in C++

Tags:

c++

struct

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

like image 612
Coder Avatar asked Sep 01 '25 01:09

Coder


1 Answers

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.

like image 131
Innocent Bystander Avatar answered Sep 02 '25 14:09

Innocent Bystander