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)
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);
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.]
A compiler doesn't know which method to call from the C class because templated method will be transormed in void f(int) in case of int type so you have two methods with the same name and same arguments but members of different parent classes.
template<typename T> void f(T x) {}
or
void f(int)
try this:
c.B::f<int>(3);
or this for the A class:
c.A::f(3);
Consider this simpler example:
struct A{
void f(int x){}
};
struct B{
void f(float t){}
};
struct C:public A,public B{
};
struct D{
void f(float n){}
void f(int n){}
};
int main(){
C c;
c.f(3);
D d;
d.f(3);
}
In this example, same as yours, D
compiles but C
does not.
If a class is a derived one, member lookup mechanism behaves different. It checks each base class and merges them: In the case of C
; Each base class matches the lookup ( A::f(int) and B::f(float) ). Upon merging them C
decides they are ambiguous.
For the case class D
: int
version is selected instead of float
because parameter is an integer.
What is probably happening is that the template instantiation is happening separately for class A
and B
, thus ending in two void f(int)
functions.
This does not happen in D
since there the compiler knows about the void f(int)
function as a specialization and therefore does not specialize T
for int
.
来源:https://stackoverflow.com/questions/9975291/ambiguous-when-two-superclasses-have-a-member-function-with-the-same-name-but