Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is this call to member function ambiguous?

Consider this class:

class Base{
public:
    void func(double a) = delete;
    void func(int a) const {}
};

int main(){
    Base base;

    base.func(1);
    return 0;
}

When compiled using clang++, it produces the following error:

clang++ --std=c++11 test.cpp 
test.cpp:22:7: error: call to member function 'func' is ambiguous
    base.func(1);

With g++, a warning is produced:

g++ -std=c++11 test.cpp 
test.cpp: In function ‘int main()’:
test.cpp:22:13: warning: ISO C++ says that these are ambiguous, even though the worst conversion for the first is better than the worst conversion for the second: base.func(1);

Why is this code ambiguous ?

like image 513
abraka dabra Avatar asked Mar 03 '17 08:03

abraka dabra


People also ask

How can call to overload function be ambiguous?

In Function overloading, sometimes a situation can occur when the compiler is unable to choose between two correctly overloaded functions. This situation is said to be ambiguous. Ambiguous statements are error-generating statements and the programs containing ambiguity will not compile.

How do you fix an ambiguous error in C++?

You can resolve ambiguity by qualifying a member with its class name using the scope resolution ( :: ) operator. The statement dptr->j = 10 is ambiguous because the name j appears both in B1 and B2 .

What is function ambiguous?

It represents the distortion of a returned pulse due to the receiver matched filter (commonly, but not exclusively, used in pulse compression radar) of the return from a moving target. The ambiguity function is defined by the properties of the pulse and of the filter, and not any particular target scenario.


2 Answers

Non-static member functions, like the two:

void func(double);    // #1
void func(int) const; // #2

accept also an implicit object parameter which is considered in overload resolution ([over.match]/p1) as any other argument:

Overload resolution is a mechanism for selecting the best function to call given a list of expressions that are to be the arguments of the call and a set of candidate functions that can be called based on the context of the call. The selection criteria for the best function are the number of arguments, how well the arguments match the parameter-type-list of the candidate function, how well (for non-static member functions) the object matches the implicit object parameter, and certain other properties of the candidate function.

After incorporating the implicit object parameter into the member functions signatures, the compiler sees two overloads:

void func(Base&, double);    // #1
void func(const Base&, int); // #2

and tries to select the best viable function based on the call:

Base base;
base.func(1);

The conversion from base (which is a non-const lvalue of type Base) to Base& has an Exact Match rank (direct reference binding yields an Identity conversion) -- see Table 13. The conversion from base to const Base& is also of an Exact Match rank, however, [over.ics.rank]/p3.2.6 declares #1 to have a better conversion sequence:

— S1 and S2 are reference bindings ([dcl.init.ref]), and the types to which the references refer are the same type except for top-level cv-qualifiers, and the type to which the reference initialized by S2 refers is more cv-qualified than the type to which the reference initialized by S1 refers. [ Example:

int f(const int &);
int f(int &);
int g(const int &);
int g(int);

int i;
int j = f(i);    // calls f(int &)
int k = g(i);    // ambiguous

Now for the second parameter, a conversion from an integral prvalue 1 to double is a Floating-integral conversion ([conv.fpint]) which is given a Conversion rank. On the other hand, 1 to int is an Identity conversion which is of an Exact Match rank. For this argument, #2 is considered to have a better conversion sequence ([over.ics.rank]/p3.2.2):

— the rank of S1 is better than the rank of S2, or S1 and S2 have the same rank and are distinguishable by the rules in the paragraph below, or, if not that, [...]

Overload resolution to succeed requires that there exists at most one parameter for which conversion sequences differ ([over.match.best]):

Given these definitions, a viable function F1 is defined to be a better function than another viable function F2 if for all arguments i, ICSi(F1) is not a worse conversion sequence than ICSi(F2), and then

— for some argument j, ICSj(F1) is a better conversion sequence than ICSj(F2), or, if not that, [...]

Here, ICS0(#1) is better than ICS0(#2), but in turn, ICS1(#2) is better than ICS1(#1), so the compiler can't choose between the two overloads and detects ambiguity.

like image 189
Piotr Skotnicki Avatar answered Oct 02 '22 23:10

Piotr Skotnicki


When a function is overloaded, the overload resolution takes place first. The program is ill-formed if the deleted function is the best match and is selected.

Therefore, your program would produce the same error as the following, because there is an implicit conversion from int to double and the compiler does not know what function you intend to call:

class Base{
public:
    void func(double a) {}
    void func(int a) const {}
};
like image 45
Marius Bancila Avatar answered Oct 03 '22 01:10

Marius Bancila