Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Error: request for member '' is ambiguous [duplicate]

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;
}
like image 854
Xeo Avatar asked Mar 20 '11 13:03

Xeo


3 Answers

Member lookup rules are defined in Section 10.2/2

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 declara-tion 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.

class A {
public:
  int f(int);

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

};
class C : public A, public B {};
int main()
{
     C c;
     c.f(); // ambiguous
}

So you can use the using declarations A::f and B::f to resolve that ambiguity

class C : public A, public B {
     using A::f;
     using B::f;

};

int main()
{
     C c;
     c.f(); // fine
}

The second code works flawlessly because void foo(float) is inside C's scope. Actually d.foo(5); calls void foo(float) and not the int version.

like image 143
Prasoon Saurav Avatar answered Oct 27 '22 10:10

Prasoon Saurav


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 means d.D::foo, or d.B1::foo, or d.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 find D::foo() if there were such a function. But there is none. So, working backwards up the scopes, it tries the base classes. Now foo could equally look up to B1::foo or B2::foo so it is ambiguous.

For the same reason, you would get ambiguity calling unqualified foo(5); inside a D member function.


The effect of the recommended solution:

struct Derived : public Base1, public Base2{
    using Base1::foo;
    using Base2::foo;

is that this creates the name D::foo, and makes it identify two functions. The result is that d.foo resolves to d.D::foo, and then overload resolution can happen on these two functions that are identified by D::foo.

Note: In this example D::foo(int) and Base1::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.

like image 34
M.M Avatar answered Oct 27 '22 09:10

M.M


Will it work for you?

struct Derived : public Base1, public Base2{
   using Base2::foo;}
like image 44
a1ex07 Avatar answered Oct 27 '22 10:10

a1ex07