Using-declaration
Introduce un nombre que está definido en otro lugar en la región declarativa donde aparece esta declaración using. Consulte using enum y (since C++20) using namespace para otras declaraciones relacionadas.
using
typename
(opcional)
especificador-de-nombre-anidado
identificador-no-calificado
;
|
(hasta C++17) | ||||||||
using
lista-de-declaradores
;
|
(desde C++17) | ||||||||
typename
|
- | la palabra clave typename puede utilizarse según sea necesario para resolver nombres dependientes , cuando la declaración using introduce un tipo miembro de una clase base en una plantilla de clase |
| nested-name-specifier | - |
una secuencia de nombres y operadores de resolución de ámbito
::
, que termina con un operador de resolución de ámbito. Un único
::
se refiere al espacio de nombres global.
|
| unqualified-id | - | una expresión de identificación |
| declarator-list | - |
lista separada por comas de uno o más declaradores del
typename
(opcional)
nested-name-specifier
unqualified-id
. Algunos o todos los declaradores pueden ir seguidos de puntos suspensivos
...
para indicar
expansión de paquete
|
Contenidos |
Explicación
Las declaraciones using pueden utilizarse para introducir miembros de un espacio de nombres en otros espacios de nombres y ámbitos de bloque, o para introducir miembros de una clase base en definiciones de clases derivadas , o para introducir enumerators en espacios de nombres, ámbitos de bloque y ámbitos de clase (since C++20) .
|
Una declaración using con más de un using-declarator es equivalente a una secuencia correspondiente de declaraciones using con un using-declarator. |
(since C++17) |
En ámbito de namespace y bloque
Using-declarations introducen un miembro de otro espacio de nombres en el espacio de nombres actual o ámbito de bloque.
#include <iostream> #include <string> using std::string; int main() { string str = "Ejemplo"; using std::cout; cout << str; }
Consulte namespace para más detalles.
En definición de clase
La declaración using introduce un miembro de una clase base en la definición de la clase derivada, como para exponer un miembro protegido de la base como miembro público de la derivada. En este caso, nested-name-specifier debe nombrar una clase base de la que se está definiendo. Si el nombre es el nombre de una función miembro sobrecargada de la clase base, todas las funciones miembro de la clase base con ese nombre son introducidas. Si la clase derivada ya tiene un miembro con el mismo nombre, lista de parámetros y calificaciones, el miembro de la clase derivada oculta o sobrescribe (no entra en conflicto con) el miembro que se introduce desde la clase base.
#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 es protegido typedef int value_type; }; struct D : B { using B::m; // D::m es público using B::value_type; // D::value_type es público using B::f; void f(int) override { std::cout << "D::f\n"; } // D::f(int) sobreescribe B::f(int) using B::g; void g(int) { std::cout << "D::g\n"; } // tanto g(int) como g(char) son visibles using B::h; void h(int) { std::cout << "D::h\n"; } // D::h(int) oculta B::h(int) }; int main() { D d; B& b = d; // b.m = 2; // Error: B::m es protegido d.m = 1; // B::m protegido es accesible como D::m público b.f(1); // llama a f() derivada d.f(1); // llama a f() derivada std::cout << "----------\n"; d.g(1); // llama a g(int) derivada d.g('a'); // llama a g(char) base, expuesta mediante using B::g; std::cout << "----------\n"; b.h(1); // llama a h() base d.h(1); // llama a h() derivada }
Salida:
D::f D::f ---------- D::g B::g ---------- B::h D::h
Herencia de constructoresSi la using-declaration se refiere a un constructor de una base directa de la clase que se está definiendo (por ejemplo using Base :: Base ; ), todos los constructores de esa base (ignorando el acceso de miembro) se hacen visibles para la resolución de sobrecarga al inicializar la clase derivada. Si la resolución de sobrecarga selecciona un constructor heredado, es accesible si sería accesible cuando se utiliza para construir un objeto de la clase base correspondiente: se ignora la accesibilidad de la declaración using que lo introdujo.
Si la resolución de sobrecarga selecciona uno de los constructores heredados al inicializar un objeto de dicha clase derivada, entonces el
struct B1 { B1(int, ...) {} }; struct B2 { B2(double) {} }; int get(); struct D1 : B1 { using B1::B1; // hereda B1(int, ...) int x; int y = get(); }; void test() { D1 d(2, 3, 4); // OK: B1 se inicializa llamando a B1(2, 3, 4), // luego d.x se inicializa por defecto (no se realiza inicialización), // luego d.y se inicializa llamando a get() D1 e; // Error: D1 no tiene constructor por defecto } struct D2 : B2 { using B2::B2; // hereda B2(double) B1 b; }; D2 f(1.0); // error: B1 no tiene constructor por defecto struct W { W(int); }; struct X : virtual W { using W::W; // hereda W(int) X() = delete; }; struct Y : X { using X::X; }; struct Z : Y, virtual W { using Y::Y; }; Z z(0); // OK: la inicialización de Y no invoca el constructor por defecto de X
Si el subobjeto de la clase base
struct V { V() = default; V(int); }; struct Q { Q(); }; struct A : virtual V, Q { using V::V; A() = delete; }; int bar() { return 42; } struct B : A { B() : A(bar()) {} // CORRECTO }; struct C : B {}; void foo() { C c; // "bar" no se invoca, porque el subobjeto V // no se inicializa como parte de B // (el subobjeto V se inicializa como parte de C, // porque "c" es el objeto más derivado) }
Si el constructor fue heredado de múltiples subobjetos de clase base de tipo
struct A { A(int); }; struct B : A { using A::A; }; struct C1 : B { using B::B; }; struct C2 : B { using B::B; }; struct D1 : C1, C2 { using C1::C1; using C2::C2; }; D1 d1(0); // mal formado: constructor heredado de diferentes subobjetos base B struct V1 : virtual B { using B::B; }; struct V2 : virtual B { using B::B; }; struct D2 : V1, V2 { using V1::V1; using V2::V2; }; D2 d2(0); // correcto: solo hay un subobjeto B. // Esto inicializa la clase base virtual B, // que inicializa la clase base A // luego inicializa las clases base V1 y V2 // como si fuera por un constructor por defecto predeterminado
Al igual que con las declaraciones using para cualquier otra función miembro no estática, si un constructor heredado coincide con la firma de uno de los constructores de
struct B1 { B1(int); }; struct B2 { B2(int); }; struct D2 : B1, B2 { using B1::B1; using B2::B2; D2(int); // OK: D2::D2(int) oculta tanto B1::B1(int) como B2::B2(int) }; D2 d2(0); // llama a D2::D2(int) Dentro de una clase plantilla , si una declaración using se refiere a un nombre dependiente , se considera que nombra un constructor si el nested-name-specifier tiene un nombre terminal que es el mismo que el unqualified-id . template<class T> struct A : T { using T::T; // OK, hereda los constructores de T }; template<class T, class U> struct B : T, A<U> { using A<U>::A; // OK, hereda los constructores de A<U> using T::A; // no hereda el constructor de T // aunque T puede ser una especialización de A<> }; |
(desde C++11) |
Introducción de enumeradores con ámbitoAdemás de los miembros de otro espacio de nombres y los miembros de clases base, la declaración using también puede introducir enumeradores de enumeraciones en ámbitos de espacio de nombres, bloque y clase. Una declaración using también puede usarse con enumeradores sin ámbito. enum class button { up, down }; struct S { using button::up; button b = up; // OK }; using button::down; constexpr button non_up = down; // OK constexpr auto get_button(bool is_up) { using button::up, button::down; return is_up ? up : down; // OK } enum unscoped { val }; using unscoped::val; // OK, though needless |
(desde C++20) |
Notas
Solo el nombre mencionado explícitamente en la declaración using se transfiere al ámbito declarativo: en particular, los enumeradores no se transfieren cuando el nombre del tipo de enumeración se declara mediante using.
Una declaración using no puede referirse a un espacio de nombres , a un enumerador con ámbito (until C++20) , a un destructor de una clase base o a una especialización de una plantilla de miembro para una función de conversión definida por el usuario.
Una declaración using no puede nombrar una especialización de plantilla de miembro ( template-id no está permitido por la gramática):
struct B { template<class T> void f(); }; struct D : B { using B::f; // OK: nombra una plantilla // using B::f<int>; // Error: nombra una especialización de plantilla void g() { f<int>(); } };
Una declaración using tampoco puede utilizarse para introducir el nombre de una plantilla de miembro dependiente como un
template-name
(el desambiguador
template
para
dependent names
no está permitido).
template<class X> struct B { template<class T> void f(T); }; template<class Y> struct D : B<Y> { // using B<Y>::template f; // Error: desambiguador no permitido using B<Y>::f; // compila, pero f no es un nombre de plantilla void g() { // f<int>(0); // Error: f no se reconoce como nombre de plantilla, // por lo que < no inicia una lista de argumentos de plantilla f(0); // Correcto } };
Si una declaración using introduce el operador de asignación de la clase base en la clase derivada, cuya firma coincide con el operador de copia-asignación o movimiento-asignación de la clase derivada, ese operador es ocultado por el operador de copia/movimiento asignación declarado implícitamente de la clase derivada. Lo mismo aplica para una declaración using que hereda un constructor de clase base que coincide con el constructor de copia/movimiento de la clase derivada (desde C++11) .
|
La semántica de los constructores heredados fue modificada retroactivamente por un informe de defectos contra C++11 . Anteriormente, una declaración de constructor heredado causaba que un conjunto de declaraciones de constructores sintetizados se inyectaran en la clase derivada, lo que provocaba copias/movimientos redundantes de argumentos, tenía interacciones problemáticas con algunas formas de SFINAE, y en algunos casos podía ser no implementable en ABIs principales. Los compiladores más antiguos aún pueden implementar la semántica anterior.
|
(desde C++11) |
|
Las expansiones de paquetes en las declaraciones using permiten formar una clase que expone miembros sobrecargados de bases variádicas sin recursión: template<typename... Ts> struct Overloader : Ts... { using Ts::operator()...; // expone operator() de cada base }; template<typename... T> Overloader(T...) -> Overloader<T...>; // guía de deducción C++17, no necesaria en C++20 int main() { auto o = Overloader{ [] (auto const& a) {std::cout << a;}, [] (float f) {std::cout << std::setprecision(3) << f;} }; } |
(desde C++17) |
| Macro de prueba de características | Valor | Std | Característica |
|---|---|---|---|
__cpp_inheriting_constructors
|
200802L
|
(C++11) | Constructores heredados |
201511L
|
(C++17)
(DR11) |
Reformulación de constructores heredados | |
__cpp_variadic_using
|
201611L
|
(C++17) |
Expansiones de paquetes
en declaraciones
using
|
Palabras clave
Informes de defectos
Los siguientes informes de defectos que modifican el comportamiento se aplicaron retroactivamente a los estándares publicados anteriormente de C++.
| DR | Se aplica a | Comportamiento publicado | Comportamiento correcto |
|---|---|---|---|
| CWG 258 | C++98 |
una función miembro no-const de una clase derivada puede
anular y/o ocultar una función miembro const de su base |
la anulación y ocultación también requieren
que las calificaciones cv sean las mismas |
| CWG 1738 | C++11 |
no estaba claro si está permitido
instanciar explícitamente o especializar explícitamente especializaciones de plantillas de constructores heredados |
prohibido |
| CWG 2504 | C++11 |
el comportamiento de heredar constructores
de clases base virtuales no estaba claro |
aclarado |
| P0136R1 | C++11 |
la declaración de constructor heredado inyecta
constructores adicionales en la clase derivada |
hace que los constructores de la clase base
sean encontrados mediante búsqueda por nombre |
Referencias
- Estándar C++23 (ISO/IEC 14882:2024):
-
-
9.9 La declaración
using[namespace.udecl]
-
9.9 La declaración
- Estándar C++20 (ISO/IEC 14882:2020):
-
-
9.9 La declaración
using[namespace.udecl]
-
9.9 La declaración
- Estándar C++17 (ISO/IEC 14882:2017):
-
-
10.3.3 La declaración
using[namespace.udecl]
-
10.3.3 La declaración
- Estándar C++14 (ISO/IEC 14882:2014):
-
-
7.3.3 La declaración
using[namespace.udecl]
-
7.3.3 La declaración
- Estándar C++11 (ISO/IEC 14882:2011):
-
-
7.3.3 La declaración
using[namespace.udecl]
-
7.3.3 La declaración
- Estándar C++03 (ISO/IEC 14882:2003):
-
-
7.3.3 La declaración
using[namespace.udecl]
-
7.3.3 La declaración
- Estándar C++98 (ISO/IEC 14882:1998):
-
-
7.3.3 La declaración
using[namespace.udecl]
-
7.3.3 La declaración