93
votes

I have a function with the same name, but with different signature in a base and derived classes. When I am trying to use the base class's function in another class that inherits from the derived, I receive an error. See the following code:

class A
{
    public:
    void foo(string s){};
};

class B : public A
{
    public:
    int foo(int i){};
};

class C : public B
{
    public:
    void bar()
    {
        string s;
        foo(s);
    }
};

I receive the following error from the gcc compiler:

In member function `void C::bar()': no matching function for call to `C::foo(std::string&)' candidates are: int B::foo(int)

If I remove int foo(int i){}; from class B, or if I rename it from foo1, everything works fine.

What's the problem with this?

2
Technically a duplicate of this question but this one has a better title and answers.Troubadour

2 Answers

81
votes

Functions in derived classes which don't override functions in base classes but which have the same name will hide other functions of the same name in the base class.

It is generally considered bad practice to have have functions in derived classes which have the same name as functions in the bass class which aren't intended to override the base class functions as what you are seeing is not usually desirable behaviour. It is usually preferable to give different functions different names.

If you need to call the base function you will need to scope the call by using A::foo(s). Note that this would also disable any virtual function mechanism for A::foo(string) at the same time.

110
votes

It is because name lookup stops if it finds a name in one of your bases. It won't look beyond in other bases. The function in B shadows the function in A. You have to re-declare the function of A in the scope of B, so that both functions are visible from within B and C:

class A
{
    public:
    void foo(string s){};
};

class B : public A
{
    public:
    int foo(int i){};
    using A::foo;
};

class C : public B
{
    public:
    void bar()
    {
        string s;
        foo(s);
    }
};

Edit: The real description the Standard gives is (from 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.96) 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.

It has the following to say in another place (just above it):

For an id-expression [something like "foo"], name lookup begins in the class scope of this; for a qualified-id [something like "A::foo", A is a nested-name-specifier], name lookup begins in the scope of the nested-name-specifier. Name lookup takes place before access control (3.4, clause 11).

([...] put by me). Note that means that even if your foo in B is private, the foo in A will still not be found (because access control happens later).