skip to content | skip to sidebar

name hiding and the using-declaration

March 12th, 2009

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

Comments are closed.