C++
Espacios de nombres
Buscar..
Introducción
Utilizado para evitar colisiones de nombres cuando se usan bibliotecas múltiples, un espacio de nombres es un prefijo declarativo para funciones, clases, tipos, etc.
Sintaxis
- identificador de espacio de nombres ( opt ) { declaración-seq }
- identificador de espacio de nombres en línea ( opt ) { declaraciones-seq } / * desde C ++ 11 * /
- inline ( opt ) namespace atributo-specifier-seq identifier ( opt ) { declaraciones-seq } / * desde C ++ 17 * /
- espacio de nombres adjuntando-espacio de nombres-especificador :: identificador { declaración-seq } / * desde C ++ 17 * /
- identificador de espacio de nombres = calificador de espacio de nombres calificado ;
- usando el espacio de nombres nombre-anidado-especificador ( opt ) nombre-espacio-nombres ;
- atributo-especificador-seq usando el espacio de nombres nombre-anidado-especificador ( opt ) nombre-espacio-nombres ; / * desde C ++ 11 * /
Observaciones
El namespace
palabras clave tiene tres significados diferentes según el contexto:
Cuando le sigue un nombre opcional y una secuencia de declaraciones encerrada en corchetes, define un nuevo espacio de nombres o amplía un espacio de nombres existente con esas declaraciones. Si se omite el nombre, el espacio de nombres es un espacio de nombres sin nombre .
Cuando le sigue un nombre y un signo igual, declara un alias de espacio de nombres .
Cuando está precedido por el
using
y seguido de un nombre de espacio de nombres, forma una directiva de uso , que permite encontrar los nombres en el espacio de nombres dado por búsqueda de nombre no calificado (pero no vuelve a declarar esos nombres en el ámbito actual). Una directiva de uso no puede ocurrir en el alcance de la clase.
using namespace std;
se desanima ¿Por qué? Porque namespace std
es enorme! Esto significa que hay una alta probabilidad de que los nombres colisionen:
//Really bad!
using namespace std;
//Calculates p^e and outputs it to std::cout
void pow(double p, double e) { /*...*/ }
//Calls pow
pow(5.0, 2.0); //Error! There is already a pow function in namespace std with the same signature,
//so the call is ambiguous
¿Qué son los espacios de nombres?
Un espacio de nombres de C ++ es una colección de entidades de C ++ (funciones, clases, variables), cuyos nombres tienen el prefijo del nombre del espacio de nombres. Al escribir código dentro de un espacio de nombres, las entidades con nombre que pertenecen a ese espacio de nombres no tienen que tener un prefijo con el nombre del espacio de nombres, pero las entidades externas deben usar el nombre completo. El nombre completo tiene el formato <namespace>::<entity>
. Ejemplo:
namespace Example
{
const int test = 5;
const int test2 = test + 12; //Works within `Example` namespace
}
const int test3 = test + 3; //Fails; `test` not found outside of namespace.
const int test3 = Example::test + 3; //Works; fully qualified name used.
Los espacios de nombres son útiles para agrupar definiciones relacionadas. Tomemos la analogía de un centro comercial. En general, un centro comercial se divide en varias tiendas, cada una de las cuales vende artículos de una categoría específica. Una tienda podría vender productos electrónicos, mientras que otra tienda podría vender zapatos. Estas separaciones lógicas en los tipos de tiendas ayudan a los compradores a encontrar los artículos que están buscando. Los espacios de nombres ayudan a los programadores de c ++, como los compradores, a encontrar las funciones, clases y variables que buscan mediante su organización de una manera lógica. Ejemplo:
namespace Electronics
{
int TotalStock;
class Headphones
{
// Description of a Headphone (color, brand, model number, etc.)
};
class Television
{
// Description of a Television (color, brand, model number, etc.)
};
}
namespace Shoes
{
int TotalStock;
class Sandal
{
// Description of a Sandal (color, brand, model number, etc.)
};
class Slipper
{
// Description of a Slipper (color, brand, model number, etc.)
};
}
Hay un espacio de nombres único predefinido, que es el espacio de nombres global que no tiene nombre, pero puede ser denotado por ::
. Ejemplo:
void bar() {
// defined in global namespace
}
namespace foo {
void bar() {
// defined in namespace foo
}
void barbar() {
bar(); // calls foo::bar()
::bar(); // calls bar() defined in global namespace
}
}
Haciendo espacios de nombres
Crear un espacio de nombres es realmente fácil:
//Creates namespace foo
namespace Foo
{
//Declares function bar in namespace foo
void bar() {}
}
Para llamar a la bar
, primero debe especificar el espacio de nombres, seguido del operador de resolución de alcance ::
Foo::bar();
Se permite crear un espacio de nombres en otro, por ejemplo:
namespace A
{
namespace B
{
namespace C
{
void bar() {}
}
}
}
El código anterior podría simplificarse a lo siguiente:
namespace A::B::C
{
void bar() {}
}
Extendiendo espacios de nombres
Una característica útil de los namespace
de namespace
es que puede expandirlos (agregar miembros a ellos).
namespace Foo
{
void bar() {}
}
//some other stuff
namespace Foo
{
void bar2() {}
}
Usando directiva
La palabra clave 'usar' tiene tres sabores. Combinado con la palabra clave 'espacio de nombres' se escribe una 'directiva de uso':
Si no quieres escribir Foo::
delante de todas las cosas en el espacio de nombres Foo
, puedes usar using namespace Foo;
para importar cada cosa de Foo
.
namespace Foo
{
void bar() {}
void baz() {}
}
//Have to use Foo::bar()
Foo::bar();
//Import Foo
using namespace Foo;
bar(); //OK
baz(); //OK
También es posible importar entidades seleccionadas en un espacio de nombres en lugar de todo el espacio de nombres:
using Foo::bar;
bar(); //OK, was specifically imported
baz(); // Not OK, was not imported
Una advertencia: el using namespace
en los archivos de encabezado se considera un estilo incorrecto en la mayoría de los casos. Si se hace esto, el espacio de nombres se importa en cada archivo que incluye el encabezado. Dado que no hay manera de "des- using
" un espacio de nombres, esto puede conducir a la contaminación del espacio de nombres (más o símbolos inesperados en el espacio de nombres global) o, peor aún, conflictos. Vea este ejemplo para una ilustración del problema:
/***** foo.h *****/
namespace Foo
{
class C;
}
/***** bar.h *****/
namespace Bar
{
class C;
}
/***** baz.h *****/
#include "foo.h"
using namespace Foo;
/***** main.cpp *****/
#include "bar.h"
#include "baz.h"
using namespace Bar;
C c; // error: Ambiguity between Bar::C and Foo::C
Una directiva de uso no puede ocurrir en el alcance de la clase.
Búsqueda dependiente del argumento
Cuando se llama a una función sin un calificador de espacio de nombres explícito, el compilador puede elegir llamar a una función dentro de un espacio de nombres si uno de los tipos de parámetros para esa función también se encuentra en ese espacio de nombres. Esto se denomina "búsqueda dependiente de argumentos" o ADL:
namespace Test
{
int call(int i);
class SomeClass {...};
int call_too(const SomeClass &data);
}
call(5); //Fails. Not a qualified function name.
Test::SomeClass data;
call_too(data); //Succeeds
call
falla porque ninguno de sus tipos de parámetros proviene del espacio de nombres de Test
. call_too
funciona porque SomeClass
es miembro de Test
y, por lo tanto, califica para las reglas ADL.
¿Cuándo no se produce ADL?
ADL no se produce si la búsqueda normal no cualificada encuentra un miembro de la clase, una función que se ha declarado en el ámbito del bloque o algo que no es del tipo de función. Por ejemplo:
void foo();
namespace N {
struct X {};
void foo(X ) { std::cout << '1'; }
void qux(X ) { std::cout << '2'; }
}
struct C {
void foo() {}
void bar() {
foo(N::X{}); // error: ADL is disabled and C::foo() takes no arguments
}
};
void bar() {
extern void foo(); // redeclares ::foo
foo(N::X{}); // error: ADL is disabled and ::foo() doesn't take any arguments
}
int qux;
void baz() {
qux(N::X{}); // error: variable declaration disables ADL for "qux"
}
Espacio de nombres en línea
inline namespace
incluye el contenido del espacio de nombres en línea en el espacio de nombres adjunto, por lo que
namespace Outer
{
inline namespace Inner
{
void foo();
}
}
es mayormente equivalente a
namespace Outer
{
namespace Inner
{
void foo();
}
using Inner::foo;
}
pero el elemento de Outer::Inner::
y los asociados a Outer::
son idénticos.
Así que lo siguiente es equivalente.
Outer::foo();
Outer::Inner::foo();
La alternativa using namespace Inner;
No sería equivalente para algunas partes difíciles como la especialización de plantillas:
por
#include <outer.h> // See below
class MyCustomType;
namespace Outer
{
template <>
void foo<MyCustomType>() { std::cout << "Specialization"; }
}
El espacio de nombres en línea permite la especialización de
Outer::foo
// outer.h // include guard omitted for simplification namespace Outer { inline namespace Inner { template <typename T> void foo() { std::cout << "Generic"; } } }
Mientras que el
using namespace
no permite la especialización deOuter::foo
// outer.h // include guard omitted for simplification namespace Outer { namespace Inner { template <typename T> void foo() { std::cout << "Generic"; } } using namespace Inner; // Specialization of `Outer::foo` is not possible // it should be `Outer::Inner::foo`. }
El espacio de nombres en línea es una forma de permitir que varias versiones cohabiten y por defecto a la en inline
namespace MyNamespace
{
// Inline the last version
inline namespace Version2
{
void foo(); // New version
void bar();
}
namespace Version1 // The old one
{
void foo();
}
}
Y con uso
MyNamespace::Version1::foo(); // old version
MyNamespace::Version2::foo(); // new version
MyNamespace::foo(); // default version : MyNamespace::Version1::foo();
Sin nombre / espacios de nombres anónimos
Se puede usar un espacio de nombres sin nombre para garantizar que los nombres tengan un enlace interno (solo puede hacer referencia la unidad de traducción actual). Dicho espacio de nombres se define de la misma manera que cualquier otro espacio de nombres, pero sin el nombre:
namespace {
int foo = 42;
}
foo
solo es visible en la unidad de traducción en la que aparece.
Se recomienda nunca utilizar espacios de nombres sin nombre en los archivos de encabezado, ya que esto proporciona una versión del contenido para cada unidad de traducción en la que se incluye. Esto es especialmente importante si define globales no constantes.
// foo.h
namespace {
std::string globalString;
}
// 1.cpp
#include "foo.h" //< Generates unnamed_namespace{1.cpp}::globalString ...
globalString = "Initialize";
// 2.cpp
#include "foo.h" //< Generates unnamed_namespace{2.cpp}::globalString ...
std::cout << globalString; //< Will always print the empty string
Espacios de nombres anidados compactos
namespace a {
namespace b {
template<class T>
struct qualifies : std::false_type {};
}
}
namespace other {
struct bob {};
}
namespace a::b {
template<>
struct qualifies<::other::bob> : std::true_type {};
}
Puede introducir tanto la a
y b
espacios de nombres en un solo paso con el namespace a::b
comenzando en C ++ 17.
Aliasing un espacio de nombres largo
Por lo general, se usa para cambiar el nombre o acortar referencias largas de nombres de nombres, como los componentes de una biblioteca.
namespace boost
{
namespace multiprecision
{
class Number ...
}
}
namespace Name1 = boost::multiprecision;
// Both Type declarations are equivalent
boost::multiprecision::Number X // Writing the full namespace path, longer
Name1::Number Y // using the name alias, shorter
Alcance de la Declaración de Alias
Declaración de alias se ven afectadas por las declaraciones de uso anteriores
namespace boost
{
namespace multiprecision
{
class Number ...
}
}
using namespace boost;
// Both Namespace are equivalent
namespace Name1 = boost::multiprecision;
namespace Name2 = multiprecision;
Sin embargo, es más fácil confundirse sobre qué espacio de nombres está creando alias cuando tiene algo como esto:
namespace boost
{
namespace multiprecision
{
class Number ...
}
}
namespace numeric
{
namespace multiprecision
{
class Number ...
}
}
using namespace numeric;
using namespace boost;
// Not recommended as
// its not explicitly clear whether Name1 refers to
// numeric::multiprecision or boost::multiprecision
namespace Name1 = multiprecision;
// For clarity, its recommended to use absolute paths
// instead
namespace Name2 = numeric::multiprecision;
namespace Name3 = boost::multiprecision;
Alias del espacio de nombres
A un espacio de nombres se le puede dar un alias ( es decir, otro nombre para el mismo espacio de nombres) usando el identificador de namespace
=
sintaxis. Se puede acceder a los miembros del espacio de nombres con alias calificándolos con el nombre del alias. En el siguiente ejemplo, el espacio de nombres anidado AReallyLongName::AnotherReallyLongName
es inconveniente de escribir, por lo que la función qux
declara localmente un alias N
Se puede acceder a los miembros de ese espacio de nombres simplemente usando N::
.
namespace AReallyLongName {
namespace AnotherReallyLongName {
int foo();
int bar();
void baz(int x, int y);
}
}
void qux() {
namespace N = AReallyLongName::AnotherReallyLongName;
N::baz(N::foo(), N::bar());
}