Nathaniel Flath
Nathaniel Flath

Reputation: 16005

C++ Virtual function being hidden

I'm having a problem with C++ inheritance.

I have a class hierarchy:

class A {
public:
   virtual void onFoo() {}
   virtual void onFoo(int i) {}
};

class B : public A {
public:
    virtual void onFoo(int i) {}
};

class C : public B {
};


int main() {
    C* c = new C();
    c->onFoo(); //Compile error - doesn't exist
}

My question is: why doesn't this compile? My understanding is that C should inherit both onFoo functions from A - and in fact, this compiles if you remove the redefinition of onFoo in B - but g++ gives an error that C has no onFoo() function.

Upvotes: 29

Views: 14980

Answers (6)

Krishnachandra Sharma
Krishnachandra Sharma

Reputation: 1342

I guess you have missed adding this in class B:

struct B : A
{
    using A::onFoo;
    virtual void onFoo(int i) {}
    void onFoo() {} //This line missing in your code.
};

Now this compiles!

Upvotes: -3

The issue that you are experiencing is related to how name lookup works in C++. In particular, when resolving a member, the compiler will look into the static type of the object on which the member is being accessed. If the identifier is found in that class, then lookup completes and (in the case of member functions) overload resolution starts. If the identifier is not found, it will crawl up the hierarchy, class by class, trying to locate the identifier one level at a time.

In your particular case, you have c->onFoo(); and c is of type C. The compiler does not see any declaration of onFoo in C, so it continues upwards in the hierarchy. When the compiler checks B, it sees that there is a declaration of void onFoo(int i) at that level, so it stops lookup, and tries overload resolution. At this time, the overload resolution fails due to the inconsistency in the arguments.

The fact that a declaration of void onFoo(int) is present at B level has the effect of hiding the rest of the overloads in any base class, as it will stop lookup. Note that this is a problem with unqualified lookup, the function is still there and applicable to the object, but will not be found by regular lookup (you can still call it as c->A::onFoo()).

As of how to deal with hiding, the simplest way is by employing the using declaration to bring the functions into scope:

class B : A {
public:
   using A::onFoo; // All A::onFoo overloads are *considered* here
   void onFoo( int );
};

The effect of the using declaration here is that when the B class is looked up, in search for the onFoo identifier, the compiler is instructed to also consider all overloads of onFoo in the base class, enabling regular lookup to find A::onFoo().

Upvotes: 58

Ben Voigt
Ben Voigt

Reputation: 283624

If you want base class members to overload derived class members, you want to use using:

struct A
{
   virtual void onFoo() {}
   virtual void onFoo(int i) {}
};

struct B : A
{
    using A::onFoo;
    virtual void onFoo(int i) {}
};

struct C : B
{
};


int main()
{
    C* c = new C();
    c->onFoo();
}

Upvotes: 12

Grozz
Grozz

Reputation: 8425

You forgot public: modifier before methods in both class A and B. Therefore method onFoo is private and thus not visible anywhere outside these classes.

Upvotes: 1

selbie
selbie

Reputation: 104484

Methods on class A and B should be public. That, and you are missing semi-colons at the end of each class declaration.

class A {
public:
   virtual void onFoo() {}
   virtual void onFoo(int i) {}
};

class B : public A {
public:
    virtual void onFoo(int i) {}
};

class C : public B {
};


int main() {
    C* c = new C();
    c->onFoo(); //Compile error - doesn't exist
}

Upvotes: 2

Puppy
Puppy

Reputation: 146910

This is name hiding, basically only the declared overrides exist in B and the other overloads in A are hidden.

Upvotes: 7

Related Questions