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?
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.
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
.