On this page
Using-declaration
Introduces a name that is defined elsewhere into the declarative region where this using-declaration appears.
using typename (optional) nested-name-specifier unqualified-id ; |
(until C++17) | |
using declarator-list ; |
(since C++17) |
typename |
- | the keyword typename may be used as necessary to resolve dependent names, when the using-declaration introduces a member type from a base class into a class template |
nested-name-specifier | - | a sequence of names and scope resolution operators :: , ending with a scope resolution operator. A single :: refers to the global namespace. |
unqualified-id | - | an id-expression |
declarator-list | - | comma-separated list of one or more declarators of the typename (optional) nested-name-specifier unqualified-id. Some or all of the declarators may be followed by an ellipsis ... to indicate pack expansion |
Explanation
Using-declarations can be used to introduce namespace members into other namespaces and block scopes, or to introduce base class members into derived class definitions, or to introduce enumerators into namespaces, block, and class scopes(since C++20).
A using-declaration with more than one using-declarator is equivalent to a corresponding sequence of using-declarations with one using-declarator. |
(since C++17) |
In namespace and block scope
Using-declaration introduces a member of another namespace into current namespace or block scope.
#include <iostream>
#include <string>
using std::string;
int main()
{
string str = "Example";
using std::cout;
cout << str;
}
See namespace for details.
In class definition
Using-declaration introduces a member of a base class into the derived class definition, such as to expose a protected member of base as public member of derived. In this case, nested-name-specifier must name a base class of the one being defined. If the name is the name of an overloaded member function of the base class, all base class member functions with that name are introduced. If the derived class already has a member with the same name, parameter list, and qualifications, the derived class member hides or overrides (doesn't conflict with) the member that is introduced from the base class.
#include <iostream>
struct B
{
virtual void f(int) { std::cout << "B::f\n"; }
void g(char) { std::cout << "B::g\n"; }
void h(int) { std::cout << "B::h\n"; }
protected:
int m; // B::m is protected
typedef int value_type;
};
struct D : B
{
using B::m; // D::m is public
using B::value_type; // D::value_type is public
using B::f;
void f(int) override { std::cout << "D::f\n"; } // D::f(int) overrides B::f(int)
using B::g;
void g(int) { std::cout << "D::g\n"; } // both g(int) and g(char) are visible
using B::h;
void h(int) { std::cout << "D::h\n"; } // D::h(int) hides B::h(int)
};
int main()
{
D d;
B& b = d;
// b.m = 2; // Error: B::m is protected
d.m = 1; // protected B::m is accessible as public D::m
b.f(1); // calls derived f()
d.f(1); // calls derived f()
std::cout << "----------\n";
d.g(1); // calls derived g(int)
d.g('a'); // calls base g(char), exposed via using B::g;
std::cout << "----------\n";
b.h(1); // calls base h()
d.h(1); // calls derived h()
}
Output:
D::f
D::f
----------
D::g
B::g
----------
B::h
D::h
Inheriting constructorsIf the using-declaration refers to a constructor of a direct base of the class being defined (e.g. If overload resolution selects an inherited constructor, it is accessible if it would be accessible when used to construct an object of the corresponding base class: the accessibility of the using-declaration that introduced it is ignored. If overload resolution selects one of the inherited constructors when initializing an object of such derived class, then the
If the
If the constructor was inherited from multiple base class subobjects of type
As with using-declarations for any other non-static member functions, if an inherited constructor matches the signature of one of the constructors of
Within a templated class, if a using-declaration refers to a dependent name, it is considered to name a constructor if the nested-name-specifier has a terminal name that is the same as the unqualified-id.
|
(since C++11) |
Introducing scoped enumeratorsIn addition to members of another namespace and members of base classes, using-declaration can also introduce enumerators of enumerations into namespace, block, and class scopes. A using-declaration can also be used with unscoped enumerators.
|
(since C++20) |
Notes
Only the name explicitly mentioned in the using-declaration is transferred into the declarative scope: in particular, enumerators are not transferred when the enumeration type name is using-declared.
A using-declaration cannot refer to a namespace, to a scoped enumerator(until C++20), to a destructor of a base class or to a specialization of a member template for a user-defined conversion function.
A using-declaration cannot name a member template specialization (template-id is not permitted by the grammar):
struct B
{
template<class T>
void f();
};
struct D : B
{
using B::f; // OK: names a template
// using B::f<int>; // Error: names a template specialization
void g() { f<int>(); }
};
A using-declaration also can't be used to introduce the name of a dependent member template as a template-name (the template
disambiguator for dependent names is not permitted).
template<class X>
struct B
{
template<class T>
void f(T);
};
template<class Y>
struct D : B<Y>
{
// using B<Y>::template f; // Error: disambiguator not allowed
using B<Y>::f; // compiles, but f is not a template-name
void g()
{
// f<int>(0); // Error: f is not known to be a template name,
// so < does not start a template argument list
f(0); // OK
}
};
If a using-declaration brings the base class assignment operator into derived class, whose signature happens to match the derived class's copy-assignment or move-assignment operator, that operator is hidden by the implicitly-declared copy/move assignment operator of the derived class. Same applies to a using-declaration that inherits a base class constructor that happens to match the derived class copy/move constructor(since C++11).
The semantics of inheriting constructors were retroactively changed by a defect report against C++11. Previously, an inheriting constructor declaration caused a set of synthesized constructor declarations to be injected into the derived class, which caused redundant argument copies/moves, had problematic interactions with some forms of SFINAE, and in some cases can be unimplementable on major ABIs. Older compilers may still implement the previous semantics.
|
(since C++11) |
Pack expansions in using-declarations make it possible to form a class that exposes overloaded members of variadic bases without recursion:
|
(since C++17) |
Feature-test macro | Value | Std | Feature |
---|---|---|---|
__cpp_inheriting_constructors |
200802L | (C++11) | Inheriting constructors |
201511L | (C++11) (DR) |
Rewording inheriting constructors | |
__cpp_variadic_using |
201611L | (C++17) | Pack expansions in using -declarations |
Defect reports
The following behavior-changing defect reports were applied retroactively to previously published C++ standards.
DR | Applied to | Behavior as published | Correct behavior |
---|---|---|---|
CWG 258 | C++98 | a non-const member function of a derived class can override and/or hide a const member function of its base |
overriding and hiding also require cv-qualifications to be the same |
CWG 1738 | C++11 | it was not clear whether it is permitted to explicitly instantiate or explicitly specialize specializations of inheriting constructor templates |
prohibited |
CWG 2504 | C++11 | the behavior of inheriting constructors from virtual base classes was unclear |
made clear |
P0136R1 | C++11 | inheriting constructor declaration injects additional constructors in the derived class |
causes base class constructors to be found by name lookup |
© cppreference.com
Licensed under the Creative Commons Attribution-ShareAlike Unported License v3.0.
https://en.cppreference.com/w/cpp/language/using_declaration