Question

Consider:

#include <iostream>

using namespace std;

struct A {
  virtual void f() { cout << "A::f" << endl; }
  virtual void f() const { cout << "A::f const" << endl; }
};

struct B : public A {};

struct C : public A {
   virtual void f() { cout << "C::f" << endl; }
};


int main()
{
   const B b;
   b.f();   // prints "A::f const"

   const C c;
   c.f();
   // Compile-time error: passing ‘const C’ as ‘this’ argument of
   //   ‘virtual void C::f()’ discards qualifiers
}

(I'm using GCC.)

So it seems that the const version of f() gets hidden in C. This makes a lot of sense to me, but is it mandated by the standard?

Was it helpful?

Solution

I will (once more) link this great article :

First, [the compiler] looks in the immediate scope, in this case the scope of class C, and makes a list of all functions it can find that are named f (regardless of whether they're accessible or even take the right number of parameters). Only if it doesn't does it then continue "outward" into the next enclosing scope [...]

So yes, the const version of f is hidden, and that's perfectly normal. As pointed out by Simone, you can use a using statement to bring A::f in C scope.

OTHER TIPS

Yes, it is. You may write:

struct C : public A {
   virtual void f() { cout << "C::f" << endl; }
   using A::f;       
};

to make your code compile:

int main()
{
   const B b;
   b.f();   // prints "A::f const"

   const C c;
   c.f();   // prints "A::f const"
}

For more infos, you can refer to the 2010 C++ draft document's (which you can find here) chapter 10.2.(3-4).

It is not the virtuality or const-ness (or lack thereof) that hides the base member, any derived method hides a base method of the same name. This was done to ameliorate the fragile base class problem.

Imagine your code was working (possibly for years) as below, with non-relevant parts removed:

struct Base {
};

struct Derived : Base {
  void f(double);
}

void g(Derived &d) {
  d.f(42);
}

Then you need to modify Base to include a method that does something completely different, but, for some reason, you want to name it 'f':

struct Base {
  void f(int);
};

Without this rule, every use of a Derived calling f needs to be manually evaluated — and if Base is in a library given to other people, you may not even have access to those other uses! It gets worse in the face of user-defined (implicit) conversions.

Instead, it was decided to require derived classes to explicitly state they want to import given names from Base with a using declaration. This rule can be surprising and I'm not sure it's a net benefit to the language today, but they didn't ask me — at the time, I could probably only have answered them with two-syllable words, anyway. :)

Insert using B::f;

struct C : public A { 
   using A::f;
   virtual void f() { cout << "C::f" << endl; } 
}; 

C++ Standard 2003. 13.2 p.1:

Two function declarations of the same name refer to the same function if they are in the same scope and have equivalent parameter declarations (13.1). A function member of a derived class is not in the same scope as a function member of the same name in a base class.

Thus C::f hides all A::f.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top