¿Por qué incluir “usar espacio de nombres” en un archivo de encabezado es una mala idea en C++?

4 minutos de lectura

avatar de usuario
usuario388338

Mientras leía “Thinking in C++” de Bruce Eckel sobre espacios de nombres, encontré la siguiente declaración:

Sin embargo, prácticamente nunca verá una directiva de uso en un archivo de encabezado (al menos no fuera del alcance). La razón es que la directiva using elimina la protección de ese espacio de nombres en particular, y el efecto dura hasta el final de la unidad de compilación actual. Si coloca una directiva de uso (fuera de un alcance) en un archivo de encabezado, significa que esta pérdida de “protección de espacio de nombres” ocurrirá dentro de cualquier archivo que incluya este encabezado, lo que a menudo significa otros archivos de encabezado.

¿Le gustaría ayudarme a comprender la declaración anterior con algún ejemplo fácil?

  • Sí, aquí tienes: stackoverflow.com/questions/2152925/…

    – diente filoso

    2 de febrero de 2011 a las 8:53

avatar de usuario
tony delroy

Considere este programa:

line#
    1 #include <string>                                                               
    2                                                                                 
    3 using namespace std;                                                            
    4                                                                                 
    5 struct string { const char* p; };  // Beware: another string!
    6                                                                                 
    7 int main()                                                                      
    8 {                                                                               
    9     string x; // Error: ambiguous - which string is wanted?
   10 }

Si intentas compilarlo, verás errores:

g++     using.cc   -o using
using.cc: In function `int main()':
using.cc:9: error: use of `string' is ambiguous
using.cc:5: error:   first declared as `struct string' here
/usr/lib/gcc/i386-redhat-linux/3.4.6/../../../../include/c++/3.4.6/bits/stringfwd.h:60: error:
   also declared as `typedef struct std::basic_string<char, std::char_traits<char>, std::allocator<char> > std::string' here
using.cc:9: error: `string' was not declared in this scope
using.cc:9: error: expected `;' before "x"

El problema aquí es que cuando main() especifica string x;el compilador no está seguro de si el definido por el usuario ::string o incluido std::string se busca

Ahora imagina que tomas la parte superior del programa… líneas 1 a 5 – hasta e incluyendo el struct string… y colóquelo en un archivo de encabezado que luego #include antes de main(). Nada cambia: todavía tienes un error. Entonces, al igual que con los programas independientes, los archivos de encabezado con using las declaraciones en ellos pueden causar problemas para otro código que los incluye, haciendo que algunas de sus declaraciones sean ambiguas.

Sin embargo, puede ser un dolor mayor, ya que los encabezados pueden incluirse, directa o indirectamente, por cantidades arbitrariamente grandes de código dependiente, y…

  • la eliminación de la using declaración del encabezado, o
  • un cambio en el contenido de <string>o cualquier otro encabezado que afecte std::

… podría romper el código, incluido el encabezado problemático. Cualquiera de los problemas puede hacer que el código dependiente no se pueda compilar, y es posible que ni siquiera se noten los problemas hasta que se intente otra compilación. Además, la persona que sufre debido a la using la declaración puede no tener permisos de sistema de archivos/repositorio de código, autoridad corporativa, etc. para eliminar el using declaración del encabezado, ni arreglar otro código de cliente afectado.

Dicho esto, si un encabezado solo tiene “uso” dentro de una clase o función, entonces no hay efecto en el código más allá de ese alcance, por lo que el impacto potencial de los cambios en std:: se reduce drásticamente.

  • “nunca puede declarar una función o definir una clase con el mismo nombre”. ::identifiermientras que la versión “usada” se debe acceder como ::std::identifier. Curiosamente, si no hay ambigüedad, el explícito ::identifier encuentra la versión “usada”, por lo que se agrega “todo lo de ese espacio de nombres [to] el global” es la perspectiva correcta… es más que una ubicación de búsqueda implícita secundaria para identificadores sin un :: prefijo….

    – Tony Delroy

    14 de septiembre de 2012 a las 8:34

Bueno, ¿cuál es el punto de usar espacios de nombres? Es para evitar el riesgo de colisiones de nombres.

Digamos que tiene un nombre de clase bastante común, por ejemplo, FooBar. Si usa varias bibliotecas, existe el riesgo de que FooBar en la biblioteca A colisione con FooBar en la biblioteca B. Para eso usamos dos espacios de nombres diferentes A y B, para mover los FooBars del espacio de nombres global a A::FooBar y B::FooBar (para que se mantengan separados unos de otros).

Si luego pones using A; y using B; en los encabezados, esto moverá A::FooBar y B::FooBar solo a FooBar, trayendo de vuelta la colisión, eliminando la ganancia del uso de espacios de nombres en primer lugar.

¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad