Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Ambiguous when two superclasses have a member function with the same name, but different signatures

struct A {
    void f(int x) {}
};

struct B {
    template<typename T> void f(T x) {}
};

struct C : public A, public B {};

struct D {
    void f(int x){}
    template<typename T> void f(T x) {} 
};


int main(int argc, char **argv) {
    C c;
    c.f<int>(3);
    D d;
    d.f<int>(3);
}

What is the reason for which calling d.f is fine, but c.f gives

error: request for member ‘f’ is ambiguous
error: candidates are: template<class T> void B::f(T)
error:                 void A::f(int)
like image 362
Fabio Dalla Libera Avatar asked Apr 02 '12 11:04

Fabio Dalla Libera


2 Answers

The first part is due to member name lookup, that's why it fails.

I would refer you to: 10.2/2 Member name lookup

The following steps define the result of name lookup in a class scope, C. First, every declaration for the name in the class and in each of its base class sub-objects is considered. A member name f in one sub-object B hides a member name f in a sub-object A if A is a base class sub-object of B. Any declarations that are so hidden are eliminated from consideration. Each of these declarations that was introduced by a using-declaration is considered to be from each sub-object of C that is of the type containing the declaration designated by the using-declaration.

If the resulting set of declarations are not all from sub-objects of the same type, or the set has a nonstatic member and includes members from distinct sub-objects, there is an ambiguity and the program is ill-formed. Otherwise that set is the result of the lookup.

Now, for the matter with template functions.

As per 13.3.1/7 Candidate functions and argument list

In each case where a candidate is a function template, candidate function template specializations are generated using template argument deduction (14.8.3, 14.8.2). Those candidates are then handled as candidate functions in the usual way. A given name can refer to one or more function templates and also to a set of overloaded non-template functions. In such a case, the candidate functions generated from each function template are combined with the set of non-template candidate functions.

And if you continue reading 13.3.3/1 Best viable function

F1 is considered to be a better function, if:

F1 is a non-template function and F2 is a function template specialization

That's why the following snippet compiles and runs the non-template function without error:

D c;
c.f(1);
like image 80
John Leidegren Avatar answered Oct 04 '22 00:10

John Leidegren


I believe the compiler prefers A::f (non-template function) over B::f for no reason.
This seems to be a compiler implementation bug more than a implementation dependent detail.

If you add following line, then compilation goes fine and the correct function B::f<> is selected:

struct C : public A, public B { 
  using A::f; // optional
  using B::f;
};

[Funny part is that until the ::f are not brought into the scope of C, they are treated as alien functions.]

like image 31
iammilind Avatar answered Oct 03 '22 23:10

iammilind