¿Qué es la contaminación por "usar el espacio de nombres"?

15

Estaba mirando la guía de codificación de Google [aquí] y no recomiendan que se use el using namespaceo namespace::function, si no lo malinterpreté.

¿Esto se aplica stdtambién a? cout<<No funciona sin ella. Este libro , recomienda lo mismo. Entonces, ¿cómo hago para usar cout<<sin using namespace std;o std::cout<<?

¿Cuál es la forma recomendada? std::cout<<? La mayoría de los libros de texto de C ++ enseñan a los principiantes ¿ using namespace std;Están propagando prácticas de codificación deficientes?

Lord Loh
fuente

Respuestas:

18

Mientras leo el estándar de Google, no puede usar la using namespace foo;directiva en ningún lado. Esta directiva incorpora todo lo declarado en el espacio de nombres y es una causa común de colisiones y comportamientos inesperados. Otros han citado uno muy común: tiene su propio método max o min en algún lugar y colisiona en un archivo src donde alguien incluye un encabezado con su método y luego diceusing namespace std;

En ciertos lugares, se permite tener una declaración de uso, que tiene la forma using ::foo::bar;

A la gente le gusta poner directivas de uso en su código porque ahorra mucho tipeo, pero conlleva un riesgo. Si tiene un archivo con muchas declaraciones cout, puedo entender que no quiera tener que escribir std :: cout cien veces, pero simplemente puede decir usando :: std :: cout. Los trato como declaraciones de variables: abarcarlos donde se necesitan. Si una función en un archivo de 10 necesita escribir salida, no declare la forma cout en la parte superior, colóquela en esa función que está haciendo la salida real.

#include <ostream>
//using namespace std; // NO!
//using ::std::cout;   // less bad than using namespace, but I prefer to scope it

int main(int argc, char** argv)
{
   int rc = do_some_stuff(argc, argv);
   using ::std::endl;
   if (rc) { // print the success report
      using ::std::cout;
      cout << "The test run completed. The return code was " << rc << '.' << endl;
    } else {
      using ::std::cerr;
      cerr << "Unable to complete the test run." << endl;
    }
    return 0 == rc;
}

Eso es un poco extremo con solo un par de líneas haciendo salida, pero se entiende la idea.

Otra cosa que se puede hacer es alias o typedef para minimizar la escritura. No encuentro que std :: sea tan malo, pero tenemos un gran conjunto de fuentes con varias docenas de módulos y, a veces, tenemos que escribir código como console_gui::command_window::append("text"). Eso se vuelve tedioso después de un tiempo y causa muchas colas largas. Soy todo por algo como

typedef console_gui::command_window cw;
cw::append("text");

siempre y cuando los alias se realicen en un ámbito local y mantengan suficiente contexto para que el código sea legible.

Michael Mathews
fuente
1
¡Gracias! Esto es realmente útil. No solo explicó por qué es malo con buenos ejemplos, sino que también señaló soluciones con buenos ejemplos. :-)
Lord Loh.
1
Por cierto: el uso std::endlpara el vaciado explícito en stdout/ stderres normalmente bastante superfluo, esas secuencias están vinculadas a stdout/ de stderrtodos modos. Incluso ralentiza un poco las cosas.
Deduplicador
8

Esto se debe a que: 1) derrota todo el propósito de los espacios de nombres, que es reducir la colisión de nombres; 2) pone a disposición del espacio de nombres global todo el espacio de nombres especificado con la directiva using.

Por ejemplo, si incluye y define su propia función max (), colisionará con std :: max ().

http://en.cppreference.com/w/cpp/algorithm/max

La preferencia es usar std :: member_you_wish_to_use porque establece explícitamente qué espacio de nombres usar.

Pie de manzana
fuente
Supongo que esto significa que debería usar std::max()con el prefijo de espacio de nombres. ¿O me equivoco?
Lord Loh
3
Significa que si pones "using namespace std;" en su código, obtendrá errores si define su propia función máxima (o cualquier otro nombre ya definido en el espacio de nombres estándar)
Chewy Gumball
1
Simplemente significa que debes tener cuidado con las usingdirectivas porque en este caso rompería tu función max () si hubieras definido una e incluido <algorithm>. Este es un caso simple pero nunca se sabe lo que se puede romper. Necesitaría conocer toda la biblioteca para asegurarse de no romperla, pero no puede saber si su código se rompería (es decir, la colisión de nombres) en el futuro.
ApplePie
6

Citando el enlace que proporciona:

Puede usar una declaración de uso en cualquier parte de un archivo .cc y en funciones, métodos o clases en archivos .h.

// OK en archivos .cc.

// Debe estar en una función, método o clase en archivos .h.

usando :: foo :: bar;

El estilo de Google le prohíbe usar espacios de nombres de importación en un contexto global, pero permite hacerlo en los locales.

En todas partes donde el uso de la declaración solo afecta una porción limitada y claramente visible del código, es perfectamente aceptable.

Cuando contamina el contexto global, el código no relacionado se ve afectado (por la implicidad usando su encabezado). No sucede nada cuando lo haces en el contexto local.

Basilevs
fuente
Tenemos los mismos estándares. Algunas de nuestras personas escribirán localmente un largo espacio de nombres. por ejemplo, typedef foolicious :: barlicious fb; fb :: bebida d;
Michael Mathews
1

no recomiendan que se use el espacio de nombres que usa ornamespace: function` - si no lo malinterpreté.

Lo hiciste. La falta de recomendación solo se aplica a la using namespacedirectiva (que comúnmente se conoce como abusing namespace, no del todo humorístico). Se recomienda encarecidamente que utilice el nombre completo de una función u objeto, como std::cout.

H2CO3
fuente
1

Aunque la pregunta ya tiene respuestas útiles, un detalle parece ser demasiado corto.

Al principio, la mayoría de los programadores se confunden un poco con la usingpalabra clave y las descripciones de namespaceuso, incluso si intentan aprenderla buscando la referencia, porque la declaración y la directiva se leen algo equivalentes, ambas son palabras largas relativamente abstractas que comienzan con d .

Se puede acceder a los identificadores dentro de los espacios de nombres nombrando explícitamente el espacio de nombres:

myNamespace::myIdent

Esto puede ser muchas más teclas para escribir. Pero también puede disminuir la importancia de su código, si la mayoría de los identificadores se prefieren de la misma manera. La usingpalabra clave ayuda a prevenir estos inconvenientes del espacio de nombres. Dado que usingfunciona en el nivel del compilador (no es macro), su efecto dura todo el alcance en el que se utiliza. Es por eso que el estilo de Google restringe su uso a ámbitos bien definidos, es decir, clases en archivos de encabezado o funciones en archivos cpp.

... por supuesto, hay una diferencia entre usar la declaración

using myNamespace::myIdent; // make myIdent an alias of myNamespace::myIdent

y usando directiva

using myNamespace; // make all identifiers of myNamespace directly accessible

Si se usa en ámbitos enormes, este último genera mucha más confusión.

Lobo
fuente
1

Aqui tienes:

#include <iostream>

int main()
{
    std::endl(std::operator<<(std::cout, "Hello world!"));
}

Al escribirlo de esta manera, evitamos las ADL propensas a errores junto con el uso de directivas y declaraciones.

Esto está destinado a ser una respuesta sarcástica. :-RE

Estoy con Herb Sutter sobre Google en este caso. De los estándares de codificación C ++:

Puede y debe usar el espacio de nombres usando declaraciones y directivas generosamente en sus archivos de implementación después de #incluir directivas y sentirse bien al respecto. A pesar de las reiteradas afirmaciones en sentido contrario, el espacio de nombres que utiliza declaraciones y directivas no es malo y no vencen el propósito de los espacios de nombres. Más bien, son los que hacen que los espacios de nombres sean utilizables .

Puede obsesionarse con los posibles conflictos de espacio de nombres que probablemente nunca se manifestarán y probablemente no serán difíciles de solucionar en un evento tan astronómicamente raro evitando cuidadosamente las usingdirectivas y especificando explícitamente cada cosa que usa (hasta los operadores) con usingdeclaraciones, o solo ve y comienza using namespace std. Recomiendo este último desde el punto de vista de la productividad.

La mayoría de los libros de texto de C ++ enseñan a los principiantes a usar el espacio de nombres estándar; ¿Están propagando malas prácticas de codificación?

Lo contrario si me preguntas, y creo que Sutter está de acuerdo.

Ahora, en el transcurso de mi carrera, me he encontrado con un total de 3 conflictos de espacios de nombres como resultado directo de usingdirectivas en bases de código que abarcan decenas de millones de LOC. Sin embargo, en los 3 casos, estaban en archivos fuente que abarcaban más de 50,000 líneas de código heredado, originalmente escritas en C y luego convertidas en bastardas a C ++, realizando una lista ecléctica masiva de funciones dispares, incluyendo encabezados de una docena de bibliotecas diferentes, y teniendo una lista épica de #includeseso abarcó una página. A pesar del desorden épico, no fueron demasiado difíciles de arreglar, ya que causaron errores de compilación en OSX (el único sistema operativo donde el código no pudo compilarse), no errores de tiempo de ejecución. No organices tu código de esta manera de pesadilla y deberías estar bien.

Dicho esto, evite las using directivas y las declaraciones en los archivos de encabezado. Eso es simplemente retrasado. Pero para los archivos fuente, y especialmente aquellos que no tienen una página completa llena de #includedirectivas, diría que no se preocupe si no está trabajando para Google.


fuente