The following snippet produces an "ambigious call to foo" error during compilation, and I'd like to know if there is any way around this problem without fully qualifying the call to foo:
#include <iostream>
struct Base1{
void foo(int){
}
};
struct Base2{
void foo(float){
}
};
struct Derived : public Base1, public Base2{
};
int main(){
Derived d;
d.foo(5);
std::cin.get();
return 0;
}
So, question is as the title says. Ideas? I mean, the following works flawlessly:
#include <iostream>
struct Base{
void foo(int){
}
};
struct Derived : public Base{
void foo(float){
}
};
int main(){
Derived d;
d.foo(5);
std::cin.get();
return 0;
}
Will it work for you?
Member lookup rules are defined in Section 10.2/2
So you can use the
using
declarationsA::f
andB::f
to resolve that ambiguityThe second code works flawlessly because
void foo(float)
is inside C's scope. Actuallyd.foo(5);
callsvoid foo(float)
and not theint
version.Name lookup is a separate phase to overload resolution.
Name lookup occurs first. That is the process of deciding which scope the name applies to. In this case we must decide whether
d.foo
meansd.D::foo
, ord.B1::foo
, ord.B2::foo
. The name lookup rules do not take into account function parameters or anything; it is purely about names and scopes.Only once that decision has been made, do we then perform overload resolution on the different overloads of the function in the scope where the name was found.
In your example, calling
d.foo()
would findD::foo()
if there were such a function. But there is none. So, working backwards up the scopes, it tries the base classes. Nowfoo
could equally look up toB1::foo
orB2::foo
so it is ambiguous.For the same reason, you would get ambiguity calling unqualified
foo(5);
inside aD
member function.The effect of the recommended solution:
is that this creates the name
D::foo
, and makes it identify two functions. The result is thatd.foo
resolves tod.D::foo
, and then overload resolution can happen on these two functions that are identified byD::foo
.Note: In this example
D::foo(int)
andBase1::foo(int)
are two identifiers for the one function; but in general, for the name lookup and overload resolution process, it doesn't make a difference whether they are two separate functions or not.