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

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

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

¿Podría ayudarme a comprender la statement anterior con algún ejemplo fácil?

Considera este progtwig:

line# 1 #include  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 intenta comstackrlo, verá 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, std::allocator > 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 la string x; , el comstackdor no está seguro de si se desea la ::string definida por el usuario o std::string incluida.

Ahora imagine que toma la parte superior del progtwig … líneas 1 a 5 – hasta e incluyendo la struct string … y póngalo en un archivo de encabezado que luego #include before main() . Nada cambia: todavía tienes un error. Por lo tanto, al igual que para los progtwigs independientes, los archivos de encabezado con instrucciones using en ellos pueden causar problemas para otros códigos que los incluyen, lo que hace que algunas de sus declaraciones sean ambiguas.

Sin embargo, puede ser un dolor más grande, ya que los encabezados se pueden incluir, directa o indirectamente, arbitrariamente en grandes cantidades de código dependiente, y …

  • eliminar la instrucción de uso del encabezado, o
  • un cambio en el contenido de , o cualquier otro encabezado que afecte a std::

… podría romper código incluyendo el encabezado problemático. Cualquiera de los dos problemas puede hacer que el código dependiente no se pueda comstackr, y es posible que los problemas no se noten hasta que se intente otra comstackción. Además, la persona que sufre debido a la statement de using puede no tener permisos de sistema de archivos / repository de código, autoridad corporativa, etc. para eliminar la instrucción de uso del encabezado, ni corregir 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 scope, por lo que el impacto potencial de los cambios en std :: se reduce drásticamente.

Si un encabezado contiene el using namespace std , todo el espacio de nombres se agrega al espacio de nombres global en cada módulo que incluye el encabezado.

Esto significa que nunca puede declarar una función o definir una clase con el mismo nombre (y parámetros compatibles para una función) como una función / clase std en el espacio de nombres global en cualquiera de esos módulos.

Copie el siguiente párrafo de “C ++ Primer, fifth edition”:

El código dentro de los encabezados normalmente no debe usar el using declaraciones. La razón es que los contenidos de un encabezado se copian en el texto del progtwig incluido. Si un encabezado tiene una statement de uso, entonces cada progtwig que incluye ese encabezado obtiene esa misma statement de uso. Como resultado, un progtwig que no intenta utilizar el nombre de biblioteca especificado puede encontrar conflictos de nombres inesperados.

Bueno, ¿cuál es el sentido 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 utiliza 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 (por lo que se mantienen separados el uno del otro).

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