Search code examples
c++implicit-conversionoverload-resolution

Why are these overloaded function calls ambiguous?


Why are the following overloaded function calls ambiguous?? With the compile error:

call of overloaded 'test(long int)' is ambiguous,candidates are: void test(A)| void test(B)|

The code:

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;
}

Solution

  • You got an error because overload resolution has to choose from two equally viable functions (both have user-defined conversions). Function overload resolution is a very complicated subject. For more details on the overload resolution see e.g. this recent lecture by Stephan T. Lavavej. It's generally best to make single-argument constructors explicit, and then to call your function with an explicit constructor argument.

    test(0L) is not an exact match to any overload because there is no overload test(long). The two overloads you provided both have user-defined conversions on their arguments, but the compiler considers them equally viable. The A overload has to do a standard conversion (long to int) followed by a user-defined conversion (int to A), and the B overload a user-defined conversion (long to B). But both are implicit user-defined conversion sequences.

    How are these ranked? The Standard says in 13.3.3.2 Ranking implicit conversion sequences [over.ics.rank]

    Standard conversion sequence S1 is a better conversion sequence than standard conversion sequence S2 if S1 is a proper subsequence of S2

    These type of tie-breaking e.g. apply if A would be a derived class from B (or vice versa). But here neither conversion sequence is a subsequence of the other. Therefore they are equally viable and the compiler cannot resolve the call.

    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;
    }
    

    NOTE: it's int main(), not void main().