Using Declaration
Brings a single name from a namespace or base class into the current scope, giving precise control over which names are visible and accessible.
Using Declarationsince C++98A using declaration introduces exactly one name from a namespace or base class into the current declarative region, making it usable without qualification.
Overview
A using declaration has two distinct roles that share the same using keyword but serve different purposes:
- Namespace form β lifts a single name from a namespace into the current scope
- Class member form β exposes a specific base class member (or all its overloads) in the derived class scope, or adjusts its access level
The critical distinction from a using directive (using namespace std;) is precision. A using directive floods the current scope with every name in the target namespace β often hundreds of names in std. A using declaration names exactly one entity. In production code, and especially in headers, using declarations are almost always the correct choice.
C++11 extended using declarations to cover constructor inheritance. C++17 added the ability to expand parameter packs in using declarations, enabling the overload pattern.
Syntax
// Namespace form
using qualified-name;
// Class member form (inside a class definition)
using base-class::member-name;
// Inheriting constructors (C++11)
using base-class::base-class;
// Pack expansion in class scope (C++17)
using ...pack;The qualified name must be accessible at the point of the declaration. For namespace members, the name must already exist in the namespace before the using declaration. For class members, the declaration must appear in a derived class and the name must be accessible in the base (public or protected).
Examples
Namespace using declarations
The idiomatic use is selecting specific names from std without polluting scope:
#include <iostream>
#include <string>
#include <vector>
using std::cout;
using std::string;
using std::vector;
void process(const vector<string>& items) {
for (const auto& item : items) { // auto: C++11
cout << item << '\n';
}
}This is standard practice in implementation files. In header files, avoid using namespace directives entirely; a using declaration inside a function body is safe in headers if a name is needed locally.
Unhiding base class overloads
When a derived class declares a function with the same name as a base class function, it hides all base class overloads of that name β not just the one with matching parameters. A using declaration reintroduces the entire overload set:
struct Base {
void log(int value);
void log(double value);
void log(const std::string& msg);
};
struct Derived : Base {
using Base::log; // reintroduce all three overloads from Base
void log(bool flag); // adds a fourth overload without hiding Base's
};
Derived d;
d.log(42); // Base::log(int)
d.log(3.14); // Base::log(double)
d.log(true); // Derived::log(bool)
d.log("msg"); // Base::log(const std::string&)Without using Base::log, every call except d.log(true) would fail to compile β the derived class's log(bool) declaration hides the inherited overloads entirely.
Adjusting access level
A using declaration in a derived class can widen (but not narrow) the access of an inherited member:
class Engine {
protected:
void reset_state();
void flush_buffers();
};
class PublicEngine : private Engine { // private inheritance hides everything
public:
using Engine::reset_state; // selectively re-expose as public
// flush_buffers remains inaccessible
};
PublicEngine e;
e.reset_state(); // OK: promoted to public through the using declarationAttempting to narrow access β placing a public base member in a private using declaration β is ill-formed.
Inheriting constructors (C++11)
Without constructor inheritance, every constructor in a base class must be individually forwarded in the derived class. C++11 eliminates this boilerplate:
struct Widget {
Widget(int id, std::string label);
Widget(int id, std::string label, bool visible);
};
// C++11 and later
struct StyledWidget : Widget {
using Widget::Widget; // inherit all Widget constructors
// Additional constructors can still be declared
StyledWidget() : Widget(0, "default") {}
private:
int style_flags_ = 0;
};
StyledWidget sw(42, "toolbar"); // delegates to Widget(int, string)
StyledWidget sv(7, "panel", true); // delegates to Widget(int, string, bool)The inherited constructors behave as if declared in the derived class. explicit, default arguments, and exception specifications are all preserved. Derived-class member initializers run as normal β only the parameter-passing logic is delegated to the base.
Watch out for uninitialised members. If the derived class adds data members without default member initialisers, an inherited constructor leaves them indeterminate:
struct Risky : Widget {
using Widget::Widget;
int extra_; // no initialiser β indeterminate after inherited ctor runs
};Add a default member initialiser (int extra_ = 0;) or write the constructors explicitly.
Pack expansion in class scope (C++17)
The overload pattern β commonly used to visit a std::variant with multiple lambdas β requires expanding a parameter pack inside a using declaration:
// C++17
template<typename... Ts>
struct Overloaded : Ts... {
using Ts::operator()...; // expose operator() from every base
};
template<typename... Ts>
Overloaded(Ts...) -> Overloaded<Ts...>; // deduction guide
std::variant<int, double, std::string> v = 3.14;
std::visit(Overloaded{
[](int i) { std::cout << "int: " << i; },
[](double d) { std::cout << "double: " << d; },
[](const std::string& s) { std::cout << "str: " << s; }
}, v);Without using Ts::operator()..., each base class lambda's operator() would hide the others, producing a type that accepts only the last lambda's argument type.
Best Practices
Prefer using declarations over using directives in all non-trivial code. using namespace std; at file scope in a header silently injects hundreds of names into every translation unit that includes it. A collision anywhere in a large project produces errors that trace back to an innocuous-looking header line.
Scope them tightly. A using declaration inside a function body affects only that function. There is rarely a reason to place one at namespace scope in a header.
Use using Base::Base by default for thin wrappers. Inheriting constructors ensure the derived class automatically gains any new constructors added to the base in the future, keeping the two in sync without maintenance overhead.
Always audit sibling overloads when overriding. Whenever a derived class declares a function that shares a name with any base class function, check whether the other overloads need to be reintroduced. The compiler will not warn you that they are hidden.
Common Pitfalls
Confusing declarations with directives. Both using std::cout; and using namespace std; make cout usable unqualified, but the directive also pulls in every other name in the namespace. The declaration is surgical; the directive is a blunt instrument.
Uninitialised members via inherited constructors. A derived class member without a default member initialiser is left indeterminate when an inherited base constructor runs. This is a frequent source of undefined behaviour in wrapper classes.
Ambiguity with multiple inheritance. Writing using Base1::foo; using Base2::foo; for the same name foo from two different bases produces an ambiguous overload set. Any call to foo on the derived class is ill-formed. You must provide a disambiguating definition in the derived class:
struct D : Base1, Base2 {
using Base1::foo;
using Base2::foo;
void foo(int x) { Base1::foo(x); } // explicit disambiguation required
};Access narrowing is ill-formed. private: using Base::publicMember; is a hard compile error, not a silent runtime restriction. Access can only be maintained or widened, never narrowed, through a using declaration.
See Also
- ADL (Argument-Dependent Lookup) β how unqualified names are found based on argument types
- Dependent Names β name lookup in template bodies and where
typenameandtemplateare required