1
votes

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

5 Answers

6
votes

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().

1
votes

Function overloading consider exact argument types or implicit conversions. In your example both alternatives A(0L) and B(0L) are same from overload point of view, because require the implicit constructor call.

1
votes

You are calling test with a parameter of type long.

There is no test(long).

The compiler has to choose between test(A) and test(B).

To call test(A) it has a conversion sequence of long -> int -> A.

To call test(B) it has a conversion sequence of long -> B.

Depending on the ranking rules of the standard it will either pick one if one is ranked better than the other - or it will fail with ambiguity.

In this specific case the two conversion sequences are ranked equally.

There is a long list of rules in the standard about how it calculates the ranking of conversion sequences in section 13.3.3 Best viable function"

0
votes

Try this:

class A
{
  public:
  explicit A(int){}
  A(){}
};

Keyword explicit stops the compiler doing implicit conversions.

0
votes

Compiler is allowed to do only one implicit conversion to user type. If this also involves conversions between primitive types, they do not count. Even though you in case of test(B) you have two conversions in place, but the following will not compile:

class B
{
public:
    B(int) {}
};

class A
{
public:
    A(const B&) {}
};

void test(const A&) {}

....

test(5);

To disable compiler doing implicit conversion you should use explicit keyword with constructor