10.2.2. Cómo usar un espacio de nombres

Puede referirse a un nombre dentro de un espacio de nombres de tres maneras diferentes: especificando el nombre utilizando el operador de resolución de ámbito, con una directiva using que introduzca todos los nombres en el espacio de nombres o mediante una declaración using para introducir nombres de uno en uno.

Resolución del ámbito

Cualquier nombre en un espacio de nombres puede ser explícitamente especificado utilizando el operador de resolución de ámbito de la misma forma que puede referirse a los nombres dentro de una clase:

//: C10:ScopeResolution.cpp
namespace X {
  class Y {
    static int i;
  public:
    void f();
  };
  class Z;
  void func();
}
int X::Y::i = 9;
class X::Z {
  int u, v, w;
public:
  Z(int i);
  int g();
};
X::Z::Z(int i) { u = v = w = i; }
int X::Z::g() { return u = v = w = 0; }
void X::func() {
  X::Z a(1);
  a.g();
}
int main(){} ///:~

Listado 10.9. C10/ScopeResolution.cpp


Nótese que la definición X::Y::i puede referirse también a un atributo de la clase Y anidada dentro de la clase X en lugar del espacio de nombres X.

La directiva using

Puesto que teclear toda la especificación para un identificador en un espacio de nombres puede resultar rápidamente tedioso, la palabra clave using le permite importar un espacio de nombres entero de una vez. Cuando se utiliza en conjunción con la palabra clave namespace, se dice que utilizamos una directiva using. Las directivas using hacen que los nombres actúen como si perteneciesen al ámbito del espacio de nombres que les incluye más cercano por lo que puede utilizar convenientemente los nombres sin explicitar completamente su especificación. Considere el siguiente espacio de nombres:

//: C10:NamespaceInt.h
#ifndef NAMESPACEINT_H
#define NAMESPACEINT_H
namespace Int {
  enum sign { positive, negative };
  class Integer {
    int i;
    sign s;
  public:
    Integer(int ii = 0) 
      : i(ii),
        s(i >= 0 ? positive : negative)
    {}
    sign getSign() const { return s; }
    void setSign(sign sgn) { s = sgn; }
    // ...
  };
} 
#endif // NAMESPACEINT_H ///:~

Listado 10.10. C10/NamespaceInt.h


Un uso de las directivas using es incluir todos los nombres en Int dentro de otro espacio de nombres, dejando aquellos nombres anidados dentro del espacio de nombres

//: C10:NamespaceMath.h
#ifndef NAMESPACEMATH_H
#define NAMESPACEMATH_H
#include "NamespaceInt.h"
namespace Math {
  using namespace Int;
  Integer a, b;
  Integer divide(Integer, Integer);
  // ...
} 
#endif // NAMESPACEMATH_H ///:~

Listado 10.11. C10/NamespaceMath.h


También puede declarar todos los nombres en Int dentro de la función pero dejando esos nombres anidados dentro de la función:

//: C10:Arithmetic.cpp
#include "NamespaceInt.h"
void arithmetic() {
  using namespace Int;
  Integer x;
  x.setSign(positive);
}
int main(){} ///:~

Listado 10.12. C10/Arithmetic.cpp


Sin la directiva using, todos los nombres en el espacio de nombres requerirían estar completamente explicitados.

Hay un aspecto de la directiva using que podría parecer poco intuitivo al principio. La visibilidad de los nombres introducidos con una directiva using es el rango en el que se crea la directiva. Pero ¡puede hacer caso omiso de los nombres definidos en la directiva using como si estos hubiesen sido declarados globalmente para ese ámbito!

//: C10:NamespaceOverriding1.cpp
#include "NamespaceMath.h"
int main() {
  using namespace Math;
  Integer a; // Hides Math::a;
  a.setSign(negative);
  // Now scope resolution is necessary
  // to select Math::a :
  Math::a.setSign(positive);
} ///:~

Listado 10.13. C10/NamespaceOverriding1.cpp


Suponga que tiene un segundo espacio de nombres que contiene algunos nombres en namespace Math:

//: C10:NamespaceOverriding2.h
#ifndef NAMESPACEOVERRIDING2_H
#define NAMESPACEOVERRIDING2_H
#include "NamespaceInt.h"
namespace Calculation {
  using namespace Int;
  Integer divide(Integer, Integer);
  // ...
} 
#endif // NAMESPACEOVERRIDING2_H ///:~

Listado 10.14. C10/NamespaceOverriding2.h


Dado que este espacio de nombres también se introduce con una directiva using, existe la posibilidad de tener una colisión. De todos modos, la ambigüedad aparece en el momento de utilizar el nombre, no en la directiva using:

//: C10:OverridingAmbiguity.cpp
#include "NamespaceMath.h"
#include "NamespaceOverriding2.h"
void s() {
  using namespace Math;
  using namespace Calculation;
  // Everything's ok until:
  //! divide(1, 2); // Ambiguity
}
int main() {} ///:~

Listado 10.15. C10/OverridingAmbiguity.cpp


Por tanto, es posible escribir directivas using para introducir un número de espacios de nombre con nombres conflictivos sin producir ninguna ambigüedad.

La declaración using

Puede inyectar nombres de uno en uno en el ámbito actual utilizando una declaración using. A diferencia de la directiva using, que trata los nombres como si hubiesen sido declarados globalmente para ese ámbito, una declaración using es una declaración dentro del ámbito actual. Eso significa que puede sobrescribir nombres de una directiva using:

//: C10:UsingDeclaration.h
#ifndef USINGDECLARATION_H
#define USINGDECLARATION_H
namespace U {
  inline void f() {}
  inline void g() {}
}
namespace V {
  inline void f() {}
  inline void g() {}
} 
#endif // USINGDECLARATION_H ///:~

Listado 10.16. C10/UsingDeclaration.h


La declaración using simplemente da el nombre completamente especificado del identificador pero no da información de tipo. Eso significa que si el espacio de nombres contiene un grupo de funciones sobrecargadas con el mismo nombre, la declaración using declara todas las funciones pertenecientes al grupo sobrecargado.

Es posible poner una declaración using en cualquier sitio donde podría ponerse una declaración normal. Una declaración using funciona de la misma forma que cualquier declaración normal salvo por un aspecto: puesto que no se le da ninguna lista de argumentos, una declaración using puede provocar la sobrecarga de una función con los mismos tipos de argumentos (cosa que no está permitida por el procedimiento de sobrecarga normal). De todas formas, esta ambigüedad no se muestra hasta el momento de uso, no apareciendo en el instante de declaración.

Una declaración using puede también aparecer dentro de un espacio de nombres y tiene el mismo efecto que en cualquier otro lugar (ese nombre se declara dentro del espacio):

//: C10:UsingDeclaration2.cpp
#include "UsingDeclaration.h"
namespace Q {
  using U::f;
  using V::g;
  // ...
}
void m() {
  using namespace Q;
  f(); // Calls U::f();
  g(); // Calls V::g();
}
int main() {} ///:~

Listado 10.17. C10/UsingDeclaration2.cpp


Una declaración using es un alias. Le permite declarar la misma función en espacios de nombres diferentes. Si acaba redeclarando la misma función importando diferentes espacios de nombres no hay problema, no habrá ambigüedades o duplicados.