Does overriding a non-const virtual method hide a const overload?
Asked Answered
S

4

9

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?

Slovenia answered 11/11, 2010 at 9:9 Comment(3)
"Virtual" is a red herring. We're not calling any f virtually (through a base class pointer or reference) here. All lookups of f find the most derived f.Embus
Virtual and const don't really apply to the question, but I've left them as tags since I don't see much harm and don't have a need to include a more relevant tag.Verduzco
I agree about virtual, but const is what the whole question is about. Overriding f() hides f() const.Slovenia
K
6

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.

Kun answered 11/11, 2010 at 9:30 Comment(1)
+1 Nice article. As a point of interest this is also discussed in Item 33 of Effective C++ Third EditionTroutman
E
3

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

Elnora answered 11/11, 2010 at 9:21 Comment(2)
+1 for the reference to the standard, but I got more information from icecrime's answer. I was well aware of the using option. I wasn't interested in making this compile, but rather in understanding the language.Slovenia
Thank you Ari, but please remember that your question may be consulted by people with the same problem who also wants to make it compile. It's better to write something more than something less, do you agree? :)Elnora
V
3

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. :)

Verduzco answered 11/11, 2010 at 9:37 Comment(0)
A
2

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.

Adonic answered 11/11, 2010 at 9:20 Comment(1)
Thid doesn't compile. Maybe you mean "A::f".Elnora

© 2022 - 2024 — McMap. All rights reserved.