name hiding and the using-declaration
March 12th, 2009This post is inspired by a question posed by a colleague some time ago. Consider following C++ code fragment in which B
derives from A
and declares a fun
with a different signature:
#include <iostream> struct A { void fun(int x) { std::cout << "int A\n"; } }; struct B: A { void fun(float x) { std::cout << "float B\n"; } }; int main() { B b; b.fun(3); return 0; }
What shall it print? If your answer is int A
, you’re probably assuming that the compiler will choose the best possible overload between A::fun
and B::fun
. In that case however you’re ignoring an often overlooked rule called name hiding.
The truth is that when you declare a function in a derived class, any function in the parent class that goes by the same name will be hidden from B
‘s interface. From the C++ standard (C++ 98, 3.3.7 § 1): “A name can be hidden by an explicit declaration of that same name in a nested declarative region or derived class.” The consequence is that A::fun
will never be considered as a possible overload, and only suitable function to call is B::fun
. And so, the program will print float B
.
Can it be fixed? Well, it depends on what you mean by “fixed”, as nothing is broken. But it surely is possible to bring back A
‘s fun
into B
‘s interface so that it is considered as a potential overload again. One labourious way to do it is by declaring a new function in B
that forwards the call to A
:
// laborious solution struct B: A { void fun(int x) { A::fun(x); } void fun(float x) { std::cout << "float B\n"; } };
However, if there are many fun
overloads in A
, and many different derived classes like B
, this quickly gets very annoying …
A far more easier solution is the using-declaration (C++ 98, 7.3.3 and 10.2 § 2): “A using-declaration introduces a name into the declarative region in which the using-declaration appears. That name is a synonym for the name of some entity declared elsewhere.” It will declare A
‘s fun
into B
‘s scope, and make it as such available as a potential overload:
// easier solution struct B: A { using A::fun; void fun(float x) { std::cout << "float B\n"; } };
Now the compiler will also consider A::fun
as a possible overload and print int A
.
There are however a few cases in which the using-declaration won’t help: e.g. you can’t use it on constructors as they don’t have a name. For more details, see section 7.3.3 of the C++ 98 standard.