Namespaces
Los espacios de nombres proporcionan un método para prevenir conflictos de nombres en proyectos grandes.
Las entidades declaradas dentro de un bloque de namespace se colocan en un ámbito de namespace, lo que evita que se confundan con entidades de nombre idéntico en otros ámbitos.
Las entidades declaradas fuera de todos los bloques de namespace pertenecen al
global namespace
. El global namespace pertenece al
ámbito global
, y puede referirse explícitamente con un
::
inicial. Aunque no tiene declaración, el global namespace no es un
unnamed namespace
.
Se permiten múltiples bloques de namespace con el mismo nombre. Todas las declaraciones dentro de estos bloques se declaran en el mismo ámbito del namespace.
Contenidos |
Sintaxis
namespace
ns-name
{
declarations
}
|
(1) | ||||||||
inline
namespace
ns-name
{
declarations
}
|
(2) | (desde C++11) | |||||||
namespace
{
declarations
}
|
(3) | ||||||||
ns-name
::
member-name
|
(4) | ||||||||
using
namespace
ns-name
;
|
(5) | ||||||||
using
ns-name
::
member-name
;
|
(6) | ||||||||
namespace
name
=
qualified-namespace
;
|
(7) | ||||||||
namespace
ns-name
::
member-name
{
declarations
}
|
(8) | (desde C++17) | |||||||
namespace
ns-name
::
inline
member-name
{
declarations
}
|
(9) | (desde C++20) | |||||||
Explicación
Espacios de nombres
inline
(opcional)
namespace
attr
(opcional)
identifier
{
namespace-body
}
|
|||||||||
inline
|
- |
(desde C++11)
si está presente, convierte esto en un espacio de nombres en línea (ver abajo). No puede aparecer en la
definición de espacio de nombres de extensión
si la
definición de espacio de nombres original
no utilizó
inline
|
||
| attr | - | (desde C++17) secuencia opcional de cualquier número de atributos | ||
| identifier | - |
ya sea
|
||
| namespace-body | - | secuencia posiblemente vacía de declaraciones de cualquier tipo (incluyendo definiciones de clases y funciones así como espacios de nombres anidados) |
Las definiciones de espacio de nombres solo están permitidas en el ámbito del espacio de nombres, incluido el ámbito global.
Para reabrir un espacio de nombres existente (formalmente, para ser una definición de espacio de nombres de extensión ), la búsqueda del identificador utilizado en la definición del espacio de nombres debe resolverse a un nombre de espacio de nombres (no un alias de espacio de nombres), que fue declarado como miembro del espacio de nombres envolvente o de un espacio de nombres inline dentro de un espacio de nombres envolvente.
El namespace-body define un namespace scope , que afecta el name lookup .
Todos los nombres introducidos por las declaraciones que aparecen dentro del namespace-body (incluyendo definiciones de espacios de nombres anidados) se convierten en miembros del espacio de nombres identifier , ya sea que esta definición del espacio de nombres sea la definición original (que introdujo el identifier ), o una definición de extensión del espacio de nombres (que "reabrió" el espacio de nombres ya definido)
Un miembro de espacio de nombres que fue declarado dentro de un cuerpo de espacio de nombres puede ser definido o redeclarado fuera de él usando calificación explícita
namespace Q { namespace V // V es un miembro de Q, y está completamente definido dentro de Q { // namespace Q::V { // Alternativa de C++17 a las líneas anteriores class C { void m(); }; // C es un miembro de V y está completamente definido dentro de V // C::m solo está declarado void f(); // f es un miembro de V, pero solo está declarado aquí } void V::f() // definición del miembro f de V fuera de V // los espacios de nombres envolventes de f siguen siendo el espacio de nombres global, Q y Q::V { extern void h(); // Esto declara ::Q::V::h } void V::C::m() // definición de V::C::m fuera del espacio de nombres (y del cuerpo de la clase) // los espacios de nombres envolventes son el espacio de nombres global, Q y Q::V {} }
Las definiciones y redeclaraciones fuera del espacio de nombres solo están permitidas
- después del punto de declaración,
- en el ámbito del espacio de nombres, y
- en espacios de nombres que engloban el espacio de nombres original (incluyendo el espacio de nombres global).
Además, deben usar la sintaxis de identificador calificado.
namespace Q { namespace V // definición de espacio de nombres original para V { void f(); // declaración de Q::V::f } void V::f() {} // Correcto void V::g() {} // Error: g() aún no es miembro de V namespace V // definición de extensión de espacio de nombres para V { void g(); // declaración de Q::V::g } } namespace R // no es un espacio de nombres envolvente para Q { void Q::V::g() {} // Error: no se puede definir Q::V::g dentro de R } void Q::V::g() {} // Correcto: el espacio de nombres global envuelve a Q
Los nombres introducidos por las declaraciones friend dentro de una clase no local X se convierten en miembros del namespace de inclusión más interno de X, pero no se vuelven visibles para la búsqueda de nombres ordinaria (ni no calificada ni calificada ) a menos que se proporcione una declaración coincidente en el ámbito del namespace, ya sea antes o después de la definición de la clase. Dicho nombre puede encontrarse mediante ADL que considera tanto namespaces como clases.
Solo el espacio de nombres envolvente más interno es considerado por dicha declaración de amistad al decidir si el nombre entraría en conflicto con un nombre previamente declarado.
void h(int); namespace A { class X { friend void f(X); // A::f es un amigo class Y { friend void g(); // A::g es un amigo friend void h(int); // A::h es un amigo, sin conflicto con ::h }; }; // A::f, A::g y A::h no son visibles en el ámbito del namespace // aunque son miembros del namespace A X x; void g() // definición de A::g { f(x); // A::X::f se encuentra mediante ADL } void f(X) {} // definición de A::f void h(int) {} // definición de A::h // A::f, A::g y A::h ahora son visibles en el ámbito del namespace // y también son amigos de A::X y A::X::Y }
Espacios de nombres en línea
Un espacio de nombres en línea es un espacio de nombres que utiliza la palabra clave opcional
Los miembros de un espacio de nombres en línea se tratan como si fueran miembros del espacio de nombres que los contiene en muchas situaciones (enumeradas a continuación). Esta propiedad es transitiva: si un espacio de nombres N contiene un espacio de nombres en línea M, que a su vez contiene un espacio de nombres en línea O, entonces los miembros de O pueden usarse como si fueran miembros de M o N.
// in C++14, std::literals and its member namespaces are inline { using namespace std::string_literals; // makes visible operator""s // from std::literals::string_literals auto str = "abc"s; } { using namespace std::literals; // makes visible both // std::literals::string_literals::operator""s // and std::literals::chrono_literals::operator""s auto str = "abc"s; auto min = 60s; } { using std::operator""s; // makes both std::literals::string_literals::operator""s // and std::literals::chrono_literals::operator""s visible auto str = "abc"s; auto min = 60s; } Nota: la regla sobre especializaciones permite el versionado de bibliotecas: diferentes implementaciones de una plantilla de biblioteca pueden definirse en diferentes espacios de nombres en línea, mientras se permite al usuario extender el espacio de nombres principal con una especialización explícita de la plantilla primaria:
Ejecutar este código
namespace Lib { inline namespace Lib_1 { template<typename T> class A; } template<typename T> void g(T) { /* ... */ } } /* ... */ struct MyClass { /* ... */ }; namespace Lib { template<> class A<MyClass> { /* ... */ }; } int main() { Lib::A<MyClass> a; g(a); // ok, Lib is an associated namespace of A } |
(desde C++11) |
Espacios de nombres sin nombre
La unnamed-namespace-definition es una definición de espacio de nombres de la forma
inline
(opcional)
namespace
attr
(opcional)
{
namespace-body
}
|
|||||||||
inline
|
- | (since C++11) si está presente, convierte esto en un namespace inline |
| attr | - | (since C++17) secuencia opcional de cualquier número de attributes |
Esta definición se trata como una definición de un espacio de nombres con nombre único y una using-directive en el ámbito actual que nombra este espacio de nombres sin nombre (Nota: la directiva using implícitamente añadida hace que el espacio de nombres esté disponible para la búsqueda calificada de nombres y la búsqueda no calificada de nombres , pero no para la búsqueda dependiente de argumentos ). El nombre único es exclusivo en todo el programa, pero dentro de una unidad de traducción cada definición de espacio de nombres sin nombre se asigna al mismo nombre único: múltiples definiciones de espacios de nombres sin nombre en el mismo ámbito denotan el mismo espacio de nombres sin nombre.
namespace { int i; // define ::(unique)::i } void f() { i++; // incrementa ::(unique)::i } namespace A { namespace { int i; // A::(unique)::i int j; // A::(unique)::j } void g() { i++; } // A::(unique)::i++ } using namespace A; // introduce todos los nombres de A en el espacio de nombres global void h() { i++; // error: ::(unique)::i y ::A::(unique)::i están ambos en el ámbito A::i++; // ok, incrementa ::A::(unique)::i j++; // ok, incrementa ::A::(unique)::j }
|
Aunque los nombres en un espacio de nombres sin nombre pueden declararse con enlace externo, nunca son accesibles desde otras unidades de traducción porque su nombre de espacio de nombres es único. |
(until C++11) |
|
Los espacios de nombres sin nombre, así como todos los espacios de nombres declarados directa o indirectamente dentro de un espacio de nombres sin nombre, tienen enlace interno , lo que significa que cualquier nombre que se declare dentro de un espacio de nombres sin nombre tiene enlace interno. |
(since C++11) |
Declaraciones using
Introduce un nombre que está definido en otro lugar en la región declarativa donde aparece esta declaración using.
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 de la forma
typename
(opcional)
nested-name-specifier
unqualified-id
. Un declarador puede ir seguido de puntos suspensivos para indicar
expansión de paquete
, aunque esa forma solo tiene sentido en
definiciones de clases derivadas
|
Las declaraciones using pueden utilizarse para introducir miembros de espacio de nombres en otros espacios de nombres y ámbitos de bloque, o para introducir miembros de clase base en definiciones de clases derivadas , o para introducir enumerators en espacios de nombres, bloques 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. |
(desde C++17) |
Para el uso en definiciones de clases derivadas, consulte using declaration .
Los nombres introducidos en un ámbito de espacio de nombres mediante una declaración using pueden usarse como cualquier otro nombre, incluyendo la búsqueda calificada desde otros ámbitos:
void f(); namespace A { void g(); } namespace X { using ::f; // f global ahora es visible como ::X::f using A::g; // A::g ahora es visible como ::X::g using A::g, A::g; // (C++17) OK: declaración doble permitida en ámbito de namespace } void h() { X::f(); // llama a ::f X::g(); // llama a A::g }
Si, después de que se utilizó la declaración using para tomar un miembro de un espacio de nombres, el espacio de nombres se extiende y se introducen declaraciones adicionales para el mismo nombre, esas declaraciones adicionales no se vuelven visibles a través de la declaración using (a diferencia de la directiva using). Una excepción es cuando una declaración using nombra una plantilla de clase: las especializaciones parciales introducidas posteriormente son efectivamente visibles, porque su lookup procede a través de la plantilla primaria.
namespace A { void f(int); } using A::f; // ::f es ahora un sinónimo de A::f(int) namespace A // extensión del namespace { void f(char); // no cambia lo que significa ::f } void foo() { f('a'); // llama a f(int), aunque exista f(char). } void bar() { using A::f; // esta f es un sinónimo tanto para A::f(int) como para A::f(char) f('a'); // llama a f(char) }
Las declaraciones using no pueden nombrar template-id , o un namespace , o un enumerador con ámbito (hasta C++20) . Cada declarador en una declaración using introduce uno y solo un nombre, por ejemplo la declaración using para una enumeración no introduce ninguno de sus enumeradores.
Todas las restricciones sobre declaraciones regulares de los mismos nombres, ocultamiento y reglas de sobrecarga se aplican a las declaraciones using:
namespace A { int x; } namespace B { int i; struct g {}; struct x {}; void f(int); void f(double); void g(char); // OK: el nombre de función g oculta la estructura g } void func() { int i; using B::i; // error: i declarada dos veces void f(char); using B::f; // OK: f(char), f(int), f(double) son sobrecargas f(3.5); // llama a B::f(double) using B::g; g('a'); // llama a B::g(char) struct g g1; // declara que g1 tiene tipo struct B::g using B::x; using A::x; // OK: oculta la estructura B::x x = 99; // asigna a A::x struct x x1; // declara que x1 tiene tipo struct B::x }
Si una función fue introducida por una declaración using, declarar una función con el mismo nombre y lista de parámetros está mal formado (a menos que la declaración sea para la misma función). Si una plantilla de función fue introducida por una declaración using, declarar una plantilla de función con el mismo nombre, lista de tipos de parámetros, tipo de retorno y lista de parámetros de plantilla está mal formado. Dos declaraciones using pueden introducir funciones con el mismo nombre y lista de parámetros, pero si se intenta una llamada a esa función, el programa está mal formado.
namespace B { void f(int); void f(double); } namespace C { void f(int); void f(double); void f(char); } void h() { using B::f; // introduce B::f(int), B::f(double) using C::f; // introduce C::f(int), C::f(double), y C::f(char) f('h'); // llama a C::f(char) f(1); // error: ¿B::f(int) o C::f(int)? void f(int); // error: f(int) entra en conflicto con C::f(int) y B::f(int) }
Si una entidad es declarada, pero no definida en algún espacio de nombres interno, y luego es declarada mediante una using-declaration en el espacio de nombres externo, y luego aparece una definición en el espacio de nombres externo con el mismo nombre no calificado, esa definición es un miembro del espacio de nombres externo y entra en conflicto con la using-declaration:
namespace X { namespace M { void g(); // declara, pero no define X::M::g() } using M::g; void g(); // Error: intento de declarar X::g que entra en conflicto con X::M::g() }
En términos más generales, una declaración que aparece en cualquier ámbito de espacio de nombres e introduce un nombre usando un identificador no calificado siempre introduce un miembro en el espacio de nombres en el que se encuentra y no en ningún otro espacio de nombres. Las excepciones son las instanciaciones explícitas y las especializaciones explícitas de una plantilla principal que se define en un espacio de nombres en línea: debido a que no introducen un nuevo nombre, pueden usar un unqualified-id en un espacio de nombres envolvente.
Directivas using
Una using-directive es una block-declaration con la siguiente sintaxis:
attr
(opcional)
using
namespace
nested-name-specifier
(opcional)
namespace-name
;
|
(1) | ||||||||
| attr | - | (since C++11) cualquier número de attributes que se aplican a esta directiva using |
| nested-name-specifier | - |
una secuencia de nombres y operadores de resolución de ámbito
::
, terminando con un operador de resolución de ámbito. Un único
::
se refiere al espacio de nombres global. Al buscar los nombres en esta secuencia,
lookup
considera solo declaraciones de espacios de nombres
|
| namespace-name | - | un nombre de un espacio de nombres. Al buscar este nombre, lookup considera solo declaraciones de espacios de nombres |
Las directivas using están permitidas únicamente en el ámbito de espacio de nombres scope y en ámbito de bloque. Desde el punto de vista de la búsqueda de nombres no calificada de cualquier nombre después de una directiva using y hasta el final del ámbito en el que aparece, cada nombre del namespace-name es visible como si estuviera declarado en el espacio de nombres envolvente más cercano que contiene tanto la directiva using como el namespace-name .
La directiva using no añade ningún nombre a la región declarativa en la que aparece (a diferencia de la declaración using), y por lo tanto no impide que se declaren nombres idénticos.
Las directivas using son transitivas para los propósitos de la búsqueda no calificada : si un ámbito contiene una directiva using que nombra un namespace-name , que a su vez contiene directivas using para algún namespace-name-2 , el efecto es como si las directivas using del segundo espacio de nombres aparecieran dentro del primero. El orden en que ocurren estos espacios de nombres transitivos no influye en la búsqueda de nombres.
namespace A { int i; } namespace B { int i; int j; namespace C { namespace D { using namespace A; // Los nombres de A son "inyectados" en D. // La búsqueda no calificada dentro de D considera estos nombres con el mismo // ámbito que el ámbito global (por ejemplo, para propósitos de ocultamiento de nombres). // La búsqueda calificada que se refiere a D (D::nombre para algún nombre) // encontrará el mismo nombre que la búsqueda no calificada dentro de D. int j; int k; int a = i; // i es B::i, porque A::i está oculto por B::i int b = ::i; // error: todavía no existe i en el espacio de nombres global } using namespace D; // los nombres de D y A son inyectados en C int k = 89; // OK declarar un nombre idéntico a uno introducido por un using int l = k; // ambiguo: C::k o D::k int m = i; // ok: B::i oculta A::i int n = j; // ok: D::j oculta B::j } } // Todas estas son definiciones equivalentes: int t0 = B::i; int t1 = B::C::a; int t2 = B::C::D::a;
Si, después de que se utilizó una directiva using para nominar algún espacio de nombres, el espacio de nombres se extiende y se añaden miembros adicionales y/o directivas using a él, esos miembros adicionales y los espacios de nombres adicionales son visibles a través de la directiva using (en contraste con la declaración using)
namespace D { int d1; void f(char); } using namespace D; // introduce D::d1, D::f, D::d2, D::f, // E::e y E::f en el espacio de nombres global int d1; // OK: no hay conflicto con D::d1 al declarar namespace E { int e; void f(int); } namespace D // extensión del espacio de nombres { int d2; using namespace E; // directiva using transitiva void f(int); } void f() { d1++; // error: ¿ambiguo ::d1 o D::d1? ::d1++; // OK D::d1++; // OK d2++; // OK, d2 es D::d2 e++; // OK: e es E::e debido al using transitivo f(1); // error: ambiguo: ¿D::f(int) o E::f(int)? f('a'); // OK: la única f(char) es D::f(char) }
Notas
La directiva using
using
namespace
std
;
en cualquier ámbito de espacio de nombres introduce cada nombre del espacio de nombres
std
en el espacio de nombres global (dado que el espacio de nombres global es el espacio de nombres más cercano que contiene tanto
std
como cualquier espacio de nombres declarado por el usuario), lo que puede llevar a colisiones de nombres no deseadas. Esta, y otras directivas using, generalmente se consideran mala práctica en el ámbito de archivo de un archivo de cabecera (
SF.7: No escribas
using
namespace
en el ámbito global en un archivo de cabecera
).
| Macro de prueba de características | Valor | Std | Característica |
|---|---|---|---|
__cpp_namespace_attributes
|
201411L
|
(C++17) | Atributos para espacios de nombres |
Palabras clave
Ejemplo
Este ejemplo muestra cómo usar un namespace para crear una clase que ya ha sido nombrada en el
std
namespace.
#include <vector> namespace vec { template<typename T> class vector { // ... }; } // of vec int main() { std::vector<int> v1; // Standard vector. vec::vector<int> v2; // User defined vector. // v1 = v2; // Error: v1 and v2 are different object's type. { using namespace std; vector<int> v3; // Same as std::vector v1 = v3; // OK } { using vec::vector; vector<int> v4; // Same as vec::vector v2 = v4; // OK } }
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 101 | C++98 |
el programa está mal formado si una declaración de función en ámbito
de espacio de nombres o ámbito de bloque y una función introducida por una declaración using declaran la misma función (sin ambigüedad) |
permitido |
| CWG 373 | C++98 |
la búsqueda solo consideraba declaraciones de espacio de nombres para
el último nombre en el operando de una directiva using (lo cual es subóptimo, porque las clases no pueden contener espacios de nombres) |
la restricción de búsqueda
se aplica a todos los nombres en los operandos de directivas using |
| CWG 460 | C++98 | una declaración using podía nombrar un espacio de nombres | prohibido |
| CWG 565 | C++98 |
una declaración using no puede introducir una función
idéntica a otra función en el mismo ámbito, pero la restricción no se aplicaba a plantillas de función |
aplicar la misma restricción
también a plantillas de función |
| CWG 986 | C++98 | la directiva using era transitiva para búsqueda calificada | solo transitiva para búsqueda no calificada |
| CWG 987 | C++98 |
las entidades declaradas en un espacio de nombres anidado eran
también miembros del espacio de nombres contenedor |
ámbitos anidados excluidos |
| CWG 1021 | C++98 |
no estaba claro si una entidad cuya definición
se introduce a un espacio de nombres mediante declaración using se considera definida en ese espacio de nombres |
no definida en ese espacio de nombres |
| CWG 1838 | C++98 |
la definición no calificada en un espacio de nombres externo
podría definir una entidad declarada, pero no definida en otro espacio de nombres e incorporada mediante using |
la definición no calificada
siempre se refiere a su espacio de nombres |
| CWG 2155 | C++98 |
la resolución de
CWG issue 1838
no se
aplicaba a declaraciones de clase y enumeración |
aplicada |
Véase también
| namespace alias | crea un alias de un espacio de nombres existente |