Unhide Base Class Functions in C++ with using
In C++, declaring a member function in a derived class automatically
hides all base class functions sharing that same name, regardless of
differences in signatures or return types. This article explains the
name hiding mechanism in C++ inheritance and demonstrates how to use the
using Base::function_name; syntax to introduce hidden base
class functions into the derived class scope, allowing full function
overloading across inheritance boundaries.
The Name Hiding Problem in C++
When a derived class defines a member function, the compiler stops searching base classes once it finds the function name in the derived scope. This behavior, known as name hiding or shadowing, suppresses all overloads in the base class—even if their parameter lists differ entirely from the derived class implementation.
class Base {
public:
void display(int x);
void display(double x);
};
class Derived : public Base {
public:
void display(const std::string& str);
};
// Usage:
Derived d;
d.display("Hello"); // Compiles
d.display(42); // Compilation error: Base overloads are hiddenIn the example above, calling d.display(42) fails to
compile because Derived::display(const std::string&)
hides both Base::display(int) and
Base::display(double).
The
using Base::function Syntax
To restore access to the base class member functions, introduce a
using declaration within the derived class definition.
The general syntax is:
class Derived : public Base {
public:
using Base::function_name;
// Derived class overloads or overrides...
};This declaration brings every overload of function_name
from Base into the scope of Derived.
Complete Implementation Example
#include <iostream>
#include <string>
class Base {
public:
void process(int value) {
std::cout << "Base::process(int): " << value << '\n';
}
void process(double value) {
std::cout << "Base::process(double): " << value << '\n';
}
};
class Derived : public Base {
public:
// Un-hide all overloads of process() from Base
using Base::process;
// Add a new overload in the derived class
void process(const std::string& value) {
std::cout << "Derived::process(string): " << value << '\n';
}
};
int main() {
Derived obj;
obj.process(10); // Calls Base::process(int)
obj.process(3.14); // Calls Base::process(double)
obj.process("Example"); // Calls Derived::process(const std::string&)
return 0;
}Access Control and Inheritance Rules
- Access Specifier Placement: The visibility of the
un-hidden base functions corresponds to the access section
(
public,protected, orprivate) in which theusingdeclaration is placed within the derived class, provided they were accessible to the derived class originally. - Exact Signatures: If a derived class defines a
function with the exact same parameter types as a base function
introduced by
using Base::function;, the derived class function overrides or replaces the base function in the derived overload set. - Constructors: C++11 introduced a related syntax,
using Base::Base;, which uses the same mechanism to inherit all constructors from the base class into the derived class.