¿Por qué está "usando el espacio de nombres estándar;" considerado una mala práctica?


2642

Otros me han dicho que escribir using namespace std;en código es incorrecto, y que debería usar std::couty std::cindirectamente en su lugar.

¿Por qué se using namespace std;considera una mala práctica? ¿Es ineficiente o corre el riesgo de declarar variables ambiguas (variables que comparten el mismo nombre que una función en el stdespacio de nombres)? ¿Afecta el rendimiento?


512
No olvides que puedes hacer: "usando std :: cout;" lo que significa que no tiene que escribir std :: cout, pero no trae todo el espacio de nombres std al mismo tiempo.
Bill

2

64
Es particularmente malo usar 'using namespace std' en el alcance del archivo en los archivos de encabezado. Usarlo en archivos fuente (* .cpp) en el alcance del archivo después de todo incluye no es tan malo, ya que su efecto se limita a una sola unidad de traducción. Aún menos problemático es usarlo dentro de funciones o clases, porque su efecto se limita al alcance de la función o clase.
sh-

55
Me desanimaría a utilizar directiva using pero para espacios de nombres específicos como std::literals::chrono_literals, Poco::Data:Keywords, Poco::Unitsy otras cosas que se ocupará de los literales o trucos de legibilidad. Siempre que esté en el encabezado o en los archivos de implementación. Supongo que podría estar bien en el ámbito de una función, pero aparte de los literales y demás, no es útil.
Ludovic Zenohate Lagouardette

77
@ Jon: No tiene nada que ver con el espacio de nombres estándar en particular. Mi énfasis estaba destinado a estar "en el alcance del archivo en los archivos de encabezado". Para ponerlo como un consejo: No use "usar el espacio de nombres" (estándar u otro) en el alcance del archivo en los archivos de encabezado. Está bien usarlo en archivos de implementación. Perdón por la ambigüedad.
sh-

Respuestas:


2231

Esto no está relacionado con el rendimiento en absoluto. Pero considere esto: está utilizando dos bibliotecas llamadas Foo y Bar:

using namespace foo;
using namespace bar;

Todo funciona bien, y puedes llamar Blah()desde Foo y Quux()desde Bar sin problemas. Pero un día se actualiza a una nueva versión de Foo 2.0, que ahora ofrece una función llamada Quux(). Ahora tienes un conflicto: Foo 2.0 y Bar importan Quux()en tu espacio de nombres global. Esto requerirá un esfuerzo para solucionarlo, especialmente si los parámetros de la función coinciden.

Si hubiera usado foo::Blah()y bar::Quux(), entonces la introducción de foo::Quux()habría sido un no evento.


435
Siempre me ha gustado "import big_honkin_name as bhn" de Python para que pueda usar "bhn.something" en lugar de "big_honkin_name.something", lo que realmente reduce el tipeo. ¿C ++ tiene algo así?
paxdiablo

764
@Pax namespace io = boost :: filesystem;
AraK

152
Creo que es exagerado decir que es "un esfuerzo para arreglar". No tendrás instancias del nuevo foo :: Quux, así que simplemente desambigua todos tus usos actuales con bar :: Quux.
MattyT

289
¿Alguna persona sensata crearía una biblioteca con tipos cuyo nombre no calificado colisionaría con los tipos estándar?
erikkallen

94
@TomA: El problema #definees que no se limita a los espacios de nombres, sino que pisotea todo el código base. Un alias de espacio de nombres es lo que quieres.
sbi

1391

Estoy de acuerdo con todo lo que Greg escribió , pero me gustaría agregar: ¡Incluso puede ser peor de lo que Greg dijo!

Library Foo 2.0 podría introducir una función, Quux()que es una coincidencia inequívocamente mejor para algunas de sus llamadas Quux()que bar::Quux()su código llamado durante años. Entonces su código aún se compila , pero silenciosamente llama a la función incorrecta y hace dios sabe qué. Eso es tan malo como las cosas pueden ponerse.

Tenga en cuenta que el stdespacio de nombres tiene toneladas de identificadores, muchos de los cuales son muy más comunes (piense list, sort, string, iterator, etc.) que son muy probable que aparezca en otro código, también.

Si considera que esto es poco probable: se hizo una pregunta aquí en Stack Overflow en la que sucedió más o menos exactamente (función incorrecta llamada debido al std::prefijo omitido ) aproximadamente medio año después de que di esta respuesta. Aquí hay otro ejemplo más reciente de tal pregunta. Entonces este es un problema real.


Aquí hay un punto de datos más: Hace muchos, muchos años, también me resultaba molesto tener que prefijar todo desde la biblioteca estándar con std::. Luego trabajé en un proyecto en el que se decidió al principio que tanto las usingdirectivas como las declaraciones están prohibidas, excepto los ámbitos de funciones. ¿Adivina qué? A la mayoría de nosotros nos tomó muy pocas semanas acostumbrarnos a escribir el prefijo, y después de algunas semanas más, la mayoría de nosotros incluso acordamos que en realidad hizo que el código fuera más legible . Hay una razón para eso: si le gusta la prosa más corta o más larga es subjetiva, pero los prefijos agregan objetivamente claridad al código. No solo el compilador, sino que a usted también le resulta más fácil ver a qué identificador se hace referencia.

En una década, ese proyecto creció hasta tener varios millones de líneas de código. Dado que estas discusiones surgen una y otra vez, una vez tuve curiosidad por saber con qué frecuencia el alcance de la función (permitido) usingse usó realmente en el proyecto. Busqué las fuentes y solo encontré una o dos docenas de lugares donde se usaba. Para mí, esto indica que, una vez probado, los desarrolladores no encuentran lo std::suficientemente doloroso como para emplear el uso de directivas, incluso una vez cada 100 kLoC, incluso donde se permitió su uso.


En pocas palabras: el prefijo explícito de todo no hace daño, toma muy poco tiempo acostumbrarse y tiene ventajas objetivas. En particular, hace que el código sea más fácil de interpretar por el compilador y por los lectores humanos, y ese probablemente debería ser el objetivo principal al escribir el código.


140
Daña significativamente la densidad del código que puede empaquetar en una sola línea. Terminas escribiendo tu código de una manera muy larga; lo que reduce la legibilidad. Personalmente, creo que el código más corto (pero no demasiado corto) tiende a ser más legible (ya que hay menos cosas para leer y menos cosas para distraerse).
Lie Ryan

92
Supongo que te perdiste los viejos tiempos antes de que C ++ tuviera una stringclase estándar , y aparentemente cada biblioteca tenía la suya. Decirle qué: seguiremos escribiendo nuestro código std::, y puede ejecutar nuestro código grep -v std:: | vimcuando lo esté navegando. O puede enseñarle a su editor que std::es una palabra clave que debe tener el mismo color que el color de fondo. Lo que sea que funcione.
Mike DeSimone

80
No creo que std::sea ​​perjudicial en absoluto. Lleva información muy importante (es decir, "lo que viene después es parte de la biblioteca estándar", y sigue siendo un prefijo bastante corto y compacto. La mayoría de las veces, no es ningún problema. A veces, tiene algunas líneas de código donde se necesita hacer referencia a símbolos específicos en el stdespacio de nombres mucho, y luego una using. declaración en la que resuelve el alcance particular el problema muy bien, pero en el caso general, no es el ruido, que transmite información valiosa , además de la eliminación de ambigüedades.
JALF

147
Cada vez que veo std::, sé que será std::sin tener que pensarlo. Si veo stringo listo mappor ellos mismos, me pregunto un poco.
Mateen Ulhaq

68
@LieRyan Entonces, buena suerte escribiendo una biblioteca de geometría sin nombrar algo vector, transformo distance. Y esos son solo ejemplos de los muchos nombres muy comunes utilizados en la biblioteca estándar. Sugerir no usarlos por miedo o por una opinión sesgada de la función de espacio de nombres que es una parte integral de C ++ es bastante contraproducente.
Christian Rau

420

El problema con la colocación using namespacede los archivos de encabezado de sus clases es que obliga a cualquiera que quiera usar sus clases (al incluir sus archivos de encabezado) a 'usar' (es decir, ver todo) esos otros espacios de nombres.

Sin embargo, puede sentirse libre de poner una declaración de uso en sus archivos (privados) * .cpp.


Tenga en cuenta que algunas personas no están de acuerdo con mi dicho "siéntase libre" de esta manera, porque aunque una usingdeclaración en un archivo cpp es mejor que en un encabezado (porque no afecta a las personas que incluyen su archivo de encabezado), piensan que todavía no lo es bueno (porque dependiendo del código podría dificultar el mantenimiento de la implementación de la clase). Esta entrada de C ++ Super-FAQ dice:

La directiva de uso existe para el código C ++ heredado y para facilitar la transición a los espacios de nombres, pero probablemente no debería usarlo de forma regular, al menos no en su nuevo código C ++.

Las preguntas frecuentes sugieren dos alternativas:

  • Una declaración de uso:

    using std::cout; // a using-declaration lets you use cout without qualification
    cout << "Values:";
  • Simplemente escribiendo std ::

    std::cout << "Values:";

1
Por supuesto, nunca debe asumir el estado de la cout global tampoco, para que alguien tenga std: cout << std :: hex y no pueda std :: restore_cout_state después. Pero esa es otra fatberg.
Más

233

Recientemente me encontré con una queja sobre Visual Studio 2010 . Resultó que casi todos los archivos fuente tenían estas dos líneas:

using namespace std;
using namespace boost;

Muchas características de Boost están entrando en el estándar C ++ 0x, y Visual Studio 2010 tiene muchas características de C ++ 0x, por lo que de repente estos programas no se estaban compilando.

Por lo tanto, evitar using namespace X;es una forma de preparar el futuro, una forma de asegurarse de que un cambio en las bibliotecas y / o los archivos de encabezado en uso no va a romper un programa.


14
Esta. Boost y std tienen mucha superposición, especialmente desde C ++ 11.
einpoklum

1
Lo hice una vez y aprendí una lección por las malas. Ahora nunca uso usingfuera de una definición de función y rara vez uso using namespaceen absoluto.
Ferruccio

210

Versión corta: no use usingdeclaraciones o directivas globales en los archivos de encabezado. Siéntase libre de usarlos en los archivos de implementación. Esto es lo que Herb Sutter y Andrei Alexandrescu tienen que decir sobre este tema en los Estándares de codificación C ++ (negrita para el énfasis es mío):

Resumen

Los usos del espacio de nombres son para su conveniencia, no para que pueda infligir a otros: nunca escriba una declaración de uso o una directiva de uso antes de una directiva #include.

Corolario: en los archivos de encabezado, no escriba el nivel del espacio de nombres usando directivas o declaraciones; en su lugar, explícitamente el espacio de nombres califica todos los nombres. (La segunda regla se desprende de la primera, porque los encabezados nunca pueden saber qué otro encabezado #incluye podría aparecer después de ellos).

Discusión

En resumen: 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 utilizables los espacios de nombres .


44
Solo la opinión de un programador más aquí, pero si bien estoy 100% de acuerdo con la afirmación de que la palabra usingnunca debe aparecer en un encabezado, no estoy tan convencido de la licencia gratuita para colocar using namespace xyz;en cualquier parte de su código, especialmente si xyzes así std. Utilizo el using std::vector;formulario, ya que solo extrae un solo elemento del espacio de nombres en un alcance pseudo-global, lo que lleva a un riesgo mucho menor de colisión.
dgnuff

2
@Lightness Races en órbita, por supuesto, tiene derecho a su opinión. Hubiera sido más útil si hubiera habido algún intento de explicar por qué no está de acuerdo con los consejos dados en esta respuesta. Especialmente sería interesante entender cuál es el punto de los espacios de nombres si 'usarlos' es malo. ¿Por qué no simplemente nombrar cosas std_cout en lugar de std :: cout ... los creadores de C ++ / namespace deben haber tenido alguna idea cuando se molestaron en crearlos?
nyholku

1
@nyholku: No es necesario, la mayoría de las otras respuestas dan las mismas razones que yo. Además, no dudes en tener en cuenta el ":)" que adjunté a mi comentario. Y que no dije que los espacios de nombres son malos.
Carreras ligeras en órbita

Sí, me di cuenta de que :) pero en mi opinión, la mayoría de las respuestas (que van en contra de este sabio consejo) están equivocadas (no es que haya hecho ninguna estadística de lo que es la mayoría ahora). Si está de acuerdo en que el espacio de nombres "no está mal", ¿podría decir dónde cree que son apropiados si no está de acuerdo con esta respuesta?
nyholku

No puedo evitar sentir que using namespacees malo como gotoes malo. Ambos tienen usos válidos, pero 999 de cada 1000 veces se usarán incorrectamente. Entonces, sí, con using namespacela fuente no contaminará el espacio de nombres de otras inclusiones, ordenado. Pero aún así no lo protegerá contra la "diversión" que surge de using namespace Foo+ using namespace Barcon su llamada (Foo implícito: :) baz(xyz)y de repente la ruptura del código (sin cambios relacionados) solo porque Bar::baz()se agregó en alguna parte, lo que resulta ser mejor coincidir (y por lo tanto ahora se vuelve a llamar en su lugar)
CharonX

122

Uno no debería usar la usingdirectiva en el ámbito global, especialmente en los encabezados. Sin embargo, hay situaciones en las que es apropiado incluso en un archivo de encabezado:

template <typename FloatType> inline
FloatType compute_something(FloatType x)
{
    using namespace std; // No problem since scope is limited
    return exp(x) * (sin(x) - cos(x * 2) + sin(x * 3) - cos(x * 4));
}

Esto es mejor que la calificación explícita ( std::sin, std::cos...), porque es más corto y tiene la capacidad de trabajar con tipos de punto flotante definidos por el usuario (a través de una búsqueda dependiente de argumentos (ADL)).


99
Lo siento, pero estoy totalmente en desacuerdo con esto.
Billy ONeal

44
@ Billy: No hay otra forma de admitir la llamada a userlib :: cos (userlib :: superint). Cada característica tiene un uso.
Zan Lynx

17
@ Zan: Por supuesto que sí. using std::cos;, using std::sin, Etc. El problema es que aunque ningún bien diseñado userlibva a tener su siny cosdentro de su propio espacio de nombres, así, por lo que esto no le ayuda. (A menos que haya una using namespace userlibplantilla anterior a esta y que sea tan mala como using namespace std, y el alcance no esté limitado). Además, la única función como esta que veo que ocurra es swap, y en tales casos recomendaría simplemente crear una plantilla especialización std::swapy evitar todo el problema.
Billy ONeal

11
@BillyONeal: template<typename T> void swap(MyContainer<T>&, MyContainer<T>&)(No existe una especialización parcial de plantilla de función (FTPS), por lo que a veces es necesario recurrir a la sobrecarga.
sbi

38
@BillyONeal: Su comentario (¡7 veces votado!) Es incorrecto: la situación que describe es exactamente lo que ADL fue diseñado para cubrir. En pocas palabras, si xtiene uno o más "espacios de nombres asociados" (por ejemplo, si se define en namespace userlib) entonces cualquier llamada a la función que se ve como cos(x)va , además, buscar en esos espacios de nombres - y sin ningún using namespace userlib;antemano que sea necesaria. Zan Lynx tiene razón (y la búsqueda de nombres en C ++ es bizantina ...)
j_random_hacker

97

No lo use globalmente

Se considera "malo" solo cuando se usa globalmente . Porque:

  • Abarrota el espacio de nombres en el que está programando.
  • Los lectores tendrán dificultades para ver de dónde proviene un identificador particular, cuando usas muchos using namespace xyz .
  • Lo que sea verdad para otros lectores de su código fuente es aún más cierto para el lector más frecuente: usted mismo. Regrese en un año o dos y eche un vistazo ...
  • Si solo habla de él, using namespace stdes posible que no esté al tanto de todas las cosas que agarra, y cuando agrega otro #includeo pasa a una nueva revisión de C ++, puede obtener conflictos de nombres que no conocía.

Puedes usarlo localmente

Continúe y utilícelo localmente (casi) libremente. Esto, por supuesto, evita que repitasstd:: , y la repetición también es mala.

Un modismo para usarlo localmente

En C ++ 03 había un modismo - código repetitivo - para implementar una swapfunción para sus clases. Se sugirió que realmente usaras un local using namespace std, o al menos using std::swap:

class Thing {
    int    value_;
    Child  child_;
public:
    // ...
    friend void swap(Thing &a, Thing &b);
};
void swap(Thing &a, Thing &b) {
    using namespace std;      // make `std::swap` available
    // swap all members
    swap(a.value_, b.value_); // `std::stwap(int, int)`
    swap(a.child_, b.child_); // `swap(Child&,Child&)` or `std::swap(...)`
}

Esto hace la siguiente magia:

  • El compilador elegirá el std::swappara value_, es decir void std::swap(int, int).
  • Si tiene una sobrecarga void swap(Child&, Child&)implementada, el compilador la elegirá.
  • Si no tiene esa sobrecarga, el compilador la usará void std::swap(Child&,Child&)e intentará intercambiarlas.

Con C ++ 11 ya no hay razón para usar este patrón. La implementación de std::swapse cambió para encontrar una posible sobrecarga y elegirla.


55
"La implementación de std :: swap se cambió para encontrar una posible sobrecarga y elegirla". - ¿Qué? ¿Estás seguro de eso? Aunque es cierto que proporcionar una costumbre swapen primer lugar ya no es tan importante en C ++ 11, ya que std::swapes más flexible (usa semántica de movimiento). Pero std::swapelegir automáticamente su propio intercambio personalizado, eso es absolutamente nuevo para mí (y realmente no lo creo).
Christian Rau

@ChristianRau Creo que sí, sí. Leí esto en SO en alguna parte. Siempre podemos preguntarle a Howard , debería saberlo. Estoy cavando y cavando ahora ...
towi

14
Incluso en el caso de intercambio, el idioma más claro (y afortunadamente más común) es escribir en using std::swap;lugar de using namespace std;. El idioma más específico tiene menos efectos secundarios y, por lo tanto, hace que el código sea más fácil de mantener.
Adrian McCarthy

11
La oración final está mal. En C ++ 11, el Std Swap Two Step fue oficialmente bendecido como la forma correcta de llamar swap, y se cambiaron otros lugares en el estándar para decir que llaman swapasí (NB, como se indicó anteriormente, using std::swapes la forma correcta, no using namespace std). Pero std::swapsí fue enfáticamente no cambió a encontrar algún otro swapy utilizarlo. Si std::swapse llama, std::swapse acostumbra.
Jonathan Wakely

3
Sin using std::swapembargo, podría ser más prudente escribir localmente para reducir el espacio de nombres local y al mismo tiempo crear código autodocumentado. Usted está raramente siempre interesada en todo el espacio de nombres std, por lo que acaba de salir a escoger las piezas que estén interesados.
Lundin

79

Si importa los archivos de cabecera derecha de repente tiene nombres como hex, left, pluso counten su ámbito global. Esto puede ser sorprendente si no sabe que std::contiene estos nombres. Si también intenta usar estos nombres localmente, puede generar bastante confusión.

Si todo el material estándar está en su propio espacio de nombres, no tiene que preocuparse por las colisiones de nombres con su código u otras bibliotecas.


12
+1 sin mencionar distance. Todavía prefiero nombres no calificados siempre que sea prácticamente posible, ya que eso aumenta la legibilidad para mí. Además, creo que el hecho de que generalmente no califiquemos las cosas en el discurso oral y que estemos dispuestos a pasar tiempo resolviendo posibles ambigüedades, significa que tiene valor poder entender de qué se está hablando sin calificaciones, y se aplica a la fuente. código que significa que está estructurado de tal manera que está claro de qué se trata, incluso sin calificaciones.
Saludos y hth. - Alf

Sin embargo, para ser justos, no tiene la mayoría de esos si no los incluye <iomanip>. Aún así, buen punto.
einpoklum

48

Otra razón es la sorpresa.

Si veo cout << blah, en lugar de std::cout << blahpensar: ¿Qué es esto cout? ¿Es lo normal cout? ¿Es algo especial?


25
¿Esto es una broma? Realmente no puedo decirlo. Si no es así, personalmente supondría que es el 'cout' normal a menos que no confíes en el código, ya que de lo contrario sería un olor de código MÁS ALLÁ, IMO. ... Y si no confías en el código, ¿por qué lo estás usando en primer lugar? Tenga en cuenta que no estoy diciendo "¡CONFÍE TODO!" pero esto también parece un poco descabellado si, por ejemplo, se trata de una biblioteca bien conocida de GitHub o algo así.
Brent Rittenhouse

28
@BrentRittenhouse coutes un mal ejemplo porque todos lo reconocen. Pero imagina futureen una aplicación financiera. ¿Es un contrato para comprar o vender algo en una fecha específica? No, no lo es. Si el código dijera std::futureque no te confundirías tan fácilmente.
James Hollis

2
@BrentRittenhouse puede ser un mal ejemplo, hay al menos cuatro bibliotecas diferentes que tienen cout. Puede ser "¿es una biblioteca estándar? Libstdc ++? Stl? Algo más?" Y no, no todo el mundo sabe std :: cout, al menos inherentemente, 6 de 7 nuevos trabajadores que recibimos no. Porque los planes de estudio de la educación no los usan en educación Tengo que ahuyentar a printfs. O debugs () - de Qt.
Swift - Friday Pie

1
De Verdad? Está más o menos en el primer ejemplo del primer capítulo de tantos libros sobre C ++, en todo caso (con el uso del operador de inserción) es el único C ++ que algunos nuevos cuerpos conocen.
mckenzm

@mckenzm Podría ponerlo en un libro o notas de conferencia para reducir el desorden, pero no en el código
Martin Beckett

45

Los programadores experimentados usan lo que resuelve sus problemas y evitan lo que crea nuevos problemas, y evitan las directivas de uso de nivel de archivo de encabezado por esta razón exacta.

Los programadores experimentados también intentan evitar la calificación completa de los nombres dentro de sus archivos fuente. Una razón menor para esto es que no es elegante escribir más código cuando menos código es suficiente a menos que haya buenas razones . Una razón importante para esto es desactivar la búsqueda dependiente de argumentos (ADL).

¿Cuáles son estas buenas razones ? Algunas veces los programadores quieren desactivar ADL explícitamente, otras veces quieren desambiguar.

Entonces los siguientes están bien:

  1. Directivas de uso de nivel de función y declaraciones de uso dentro de implementaciones de funciones
  2. Declaraciones de uso de nivel de archivo fuente dentro de archivos fuente
  3. (A veces) directivas de uso de nivel de archivo de origen

43

Estoy de acuerdo en que no debe usarse globalmente, pero no es tan malo usarlo localmente, como en a namespace. Aquí hay un ejemplo de "El lenguaje de programación C ++" :

namespace My_lib {

    using namespace His_lib; // Everything from His_lib
    using namespace Her_lib; // Everything from Her_lib

    using His_lib::String; // Resolve potential clash in favor of His_lib
    using Her_lib::Vector; // Resolve potential clash in favor of Her_lib

}

En este ejemplo, resolvimos posibles conflictos de nombres y ambigüedades derivados de su composición.

Los nombres declarados explícitamente allí (incluidos los nombres declarados por declaraciones de uso como His_lib::String) tienen prioridad sobre los nombres accesibles en otro ámbito mediante una directiva de uso ( using namespace Her_lib).


29

También lo considero una mala práctica. ¿Por qué? Solo un día pensé que la función de un espacio de nombres es dividir cosas, por lo que no debería estropearlo arrojando todo en una bolsa global.

Sin embargo, si a menudo uso 'cout' y 'cin', escribo: using std::cout; using std::cin;en el archivo .cpp (nunca en el archivo de encabezado, ya que se propaga con #include). Creo que nadie cuerdo nunca nombrará una secuencia couto cin. ;)


77
Esa es una declaración de uso local , algo muy diferente de una directiva de uso .
sbi

25

Es bueno ver el código y saber lo que hace. Si veo std::cout, sé que ese es el coutflujo de la stdbiblioteca. Si veo, coutentonces no lo sé. Que podría ser la coutcorriente de la stdbiblioteca. O podría haber int cout = 0;diez líneas más arriba en la misma función. O una staticvariable nombrada couten ese archivo. Podría ser cualquier cosa.

Ahora tome una base de código de un millón de líneas, que no es particularmente grande, y está buscando un error, lo que significa que sabe que hay una línea en este millón de líneas que no hace lo que se supone que debe hacer. cout << 1;podría leer un static intnombre cout, desplazarlo un poco hacia la izquierda y tirar el resultado. Buscando un error, tendría que comprobarlo. ¿Puedes ver cómo realmente prefiero ver std::cout?

Es una de estas cosas que parecen una muy buena idea si eres maestro y nunca tuviste que escribir y mantener ningún código para vivir. Me encanta ver el código donde (1) sé lo que hace; y (2) estoy seguro de que la persona que lo escribió sabía lo que hace.


44
¿Cómo sabe que "std :: cout << 1" no está leyendo un int estático llamado cout en el espacio de nombres std desplazándolo por uno y tirando el resultado? Además, ¿cómo sabes lo que hace "<<";) ??? ... parece que esta respuesta no es un buen punto de datos para evitar 'usar'.
nyholku

44
Si alguien ha redefinido std :: cout para que sea un número entero, entonces su problema no es técnico, sino social: alguien lo tiene preparado para usted. (y probablemente también debería verificar todos los encabezados para cosas como #define true false, etc.)
Jeremy Friesner

2
Cuando veo cout sé que es std :: cout, siempre. Si me equivoco, es un problema de la persona que escribió este código, no yo :)
Tien Do

22

Se trata de gestionar la complejidad. El uso del espacio de nombres extraerá cosas que no desea y, por lo tanto, posiblemente dificultará la depuración (lo digo, posiblemente). Usar std :: por todas partes es más difícil de leer (más texto y todo eso).

Caballos para cursos: gestione su complejidad de la mejor manera posible y siéntase capaz.


18

Considerar

// myHeader.h
#include <sstream>
using namespace std;


// someoneElses.cpp/h
#include "myHeader.h"

class stringstream {  // Uh oh
};

Tenga en cuenta que este es un ejemplo simple. Si tiene archivos con 20 inclusiones y otras importaciones, tendrá que pasar por un montón de dependencias para resolver el problema. Lo peor de todo es que puede obtener errores no relacionados en otros módulos dependiendo de las definiciones que entren en conflicto.

No es horrible, pero se ahorrará dolores de cabeza al no usarlo en archivos de encabezado o en el espacio de nombres global. Probablemente esté bien hacerlo en ámbitos muy limitados, pero nunca he tenido problemas para escribir los cinco caracteres adicionales para aclarar de dónde provienen mis funciones.


18
  1. Debe poder leer el código escrito por personas que tienen diferentes estilos y opiniones de mejores prácticas que usted.

  2. Si solo estás usando cout, nadie se confunde. Pero cuando tiene muchos espacios de nombres volando y ve esta clase y no está exactamente seguro de lo que hace, tener el espacio de nombres explícito actúa como una especie de comentario. Puede ver a primera vista, "oh, esta es una operación del sistema de archivos" o "eso está haciendo cosas de red".


17

Usar muchos espacios de nombres al mismo tiempo es obviamente una receta para el desastre, pero usar SOLO espacios de nombres stdy solo espacios de nombresstd no es tan importante en mi opinión porque la redefinición solo puede ocurrir por su propio código ...

Tan solo considérelas funciones como nombres reservados como "int" o "clase" y eso es todo.

La gente debería dejar de ser tan anal al respecto. Tu maestra tenía razón todo el tiempo. Simplemente use UN espacio de nombres; ese es el objetivo de usar espacios de nombres en primer lugar. Se supone que no debe usar más de uno al mismo tiempo. A menos que sea tuyo. Entonces, nuevamente, la redefinición no sucederá.


La creación de colisiones no es tan difícil - cadenas cortas como min, endy lessaparecen en el std::espacio de nombres. Pero más, ahora que std::tiene miles de símbolos, es útil para el lector saber de dónde proviene un nuevo símbolo que tal vez no conozcan.
Tom Swirly

El espacio de nombres estándar existe porque las personas, ya sea usted, sus colegas o las personas que escriben middleware que utiliza, no siempre son sabios al poner funciones dentro de los espacios de nombres. Por lo tanto, puede importar todo std :: y nada más, al tiempo que invoca una colisión entre, por ejemplo, std :: min y el legado de alguien más :: min () de antes del momento en que estaba en std.
Aiken Drum

14

Estoy de acuerdo con los demás aquí, pero me gustaría abordar las preocupaciones con respecto a la legibilidad: puede evitar todo eso simplemente usando typedefs en la parte superior de su archivo, función o declaración de clase.

Usualmente lo uso en mi declaración de clase ya que los métodos en una clase tienden a tratar con tipos de datos similares (los miembros) y un typedef es una oportunidad para asignar un nombre que sea significativo en el contexto de la clase. Esto realmente ayuda a la legibilidad en las definiciones de los métodos de clase.

// Header
class File
{
   typedef std::vector<std::string> Lines;
   Lines ReadLines();
}

y en la implementación:

// .cpp
Lines File::ReadLines()
{
    Lines lines;
    // Get them...
    return lines;
}

Opuesto a:

// .cpp
vector<string> File::ReadLines()
{
    vector<string> lines;
    // Get them...
    return lines;
}

o:

// .cpp
std::vector<std::string> File::ReadLines()
{
    std::vector<std::string> lines;
    // Get them...
    return lines;
}

Solo un comentario menor, mientras que typedef es útil, consideraría hacer una clase que represente Líneas en lugar de usar typedef.
Eyal Solnik

14

Un ejemplo concreto para aclarar la preocupación. Imagine que tiene una situación en la que tiene dos bibliotecas fooy barcada una con su propio espacio de nombres:

namespace foo {
    void a(float) { /* Does something */ }
}

namespace bar {
    ...
}

Ahora supongamos que usa fooy barjuntos en su propio programa de la siguiente manera:

using namespace foo;
using namespace bar;

void main() {
    a(42);
}

En este punto todo está bien. Cuando ejecuta su programa, "hace algo". Pero luego actualizas bary digamos que ha cambiado para ser así:

namespace bar {
    void a(float) { /* Does something completely different */ }
}

En este punto, obtendrá un error del compilador:

using namespace foo;
using namespace bar;

void main() {
    a(42);  // error: call to 'a' is ambiguous, should be foo::a(42)
}

Por lo tanto, deberá realizar algunas tareas de mantenimiento para aclarar que 'a' significaba foo::a. Eso no es deseable, pero afortunadamente es bastante fácil (solo agregue foo::frente a todas las llamadas aa que el compilador marca como ambiguo).

Pero imagine un escenario alternativo donde la barra cambió en su lugar para verse así:

namespace bar {
    void a(int) { /* Does something completely different */ }
}

En este punto, su llamado a a(42)unirse repentinamente a, bar::aen foo::alugar de hacer "algo", hace "algo completamente diferente". No hay advertencia del compilador ni nada. Su programa comienza silenciosamente a hacer algo completamente diferente que antes.

Cuando usa un espacio de nombres, está arriesgando un escenario como este, por lo que las personas se sienten incómodas al usar espacios de nombres. Cuantas más cosas hay en un espacio de nombres, mayor es el riesgo de conflicto, por lo que las personas pueden sentirse aún más incómodas al usar el espacio de nombres std(debido a la cantidad de cosas en ese espacio de nombres) que otros espacios de nombres.

En última instancia, se trata de una compensación entre la capacidad de escritura y la fiabilidad / mantenibilidad. La legibilidad también puede tener en cuenta, pero podría ver argumentos para eso yendo en cualquier dirección. Normalmente diría que la confiabilidad y la mantenibilidad son más importantes, pero en este caso pagará constantemente el costo de escritura por un impacto bastante raro de confiabilidad / mantenibilidad. La "mejor" compensación determinará su proyecto y sus prioridades.


El segundo escenario cierra el trato para mí. Sin espacios de nombres de nuevo. No puede haber cambios tan sutiles en la funcionalidad que no se detectan bajo el capó.
safe_malloc

13

Un espacio de nombres es un ámbito con nombre. Los espacios de nombres se utilizan para agrupar declaraciones relacionadas y para mantener elementos separados por separado. Por ejemplo, dos bibliotecas desarrolladas por separado pueden usar el mismo nombre para referirse a diferentes elementos, pero un usuario aún puede usar ambos:

namespace Mylib{
    template<class T> class Stack{ /* ... */ };
    // ...
}

namespace Yourlib{
    class Stack{ /* ... */ };
    // ...
}

void f(int max) {
    Mylib::Stack<int> s1(max); // Use my stack
    Yourlib::Stack    s2(max); // Use your stack
    // ...
}

Repetir un nombre de espacio de nombres puede ser una distracción tanto para lectores como para escritores. En consecuencia, es posible afirmar que los nombres de un espacio de nombres en particular están disponibles sin calificación explícita. Por ejemplo:

void f(int max) {
    using namespace Mylib; // Make names from Mylib accessible
    Stack<int> s1(max); // Use my stack
    Yourlib::Stack s2(max); // Use your stack
    // ...
}

Los espacios de nombres proporcionan una herramienta poderosa para la gestión de diferentes bibliotecas y de diferentes versiones de código. En particular, ofrecen al programador alternativas de cuán explícito hacer una referencia a un nombre no local.

Fuente: Una descripción general del lenguaje de programación C ++ por Bjarne Stroustrup


44
Muy interesante que esta respuesta se base en la guía de ningún otro que Bjarne Stroustrup haya obtenido -2 ... el chico Bjarne debe haber sido un programador pobre e inexperto cuando introdujo esta característica en C ++
nyholku

@nyholku: Mira esto .
sbi

10

Un ejemplo donde using namespace stdarroja un error de compilación debido a la ambigüedad de la cuenta, que también es una función en la biblioteca de algoritmos.

#include <iostream>

using namespace std;

int count = 1;
int main() {
    cout << count << endl;
}

2
::count--problema resuelto. Por lo general, tendrá más cosas del espacio de nombres estándar que de cualquier otro lugar, por lo tanto, mantener la directiva de uso del espacio de nombres podría ahorrarle escribir.
PSkocik

El verdadero problema aquí es que C ++ todavía tiene globales sin espacio de nombres. Esto, y el hecho de que 'esto' está implícito en los métodos, causa tantos errores y problemas que ni siquiera puedo contarlos, incluso con la variable correcta 'contar'. ;)
Aiken Drum

9

No empeora el rendimiento de su software o proyecto. La inclusión del espacio de nombres al comienzo de su código fuente no está mal. La inclusión de la using namespace stdinstrucción varía según sus necesidades y la forma en que está desarrollando el software o proyecto.

El namespace stdcontiene funciones y variables estándar el C ++. Este espacio de nombres es útil cuando a menudo usaría las funciones estándar de C ++.

Como se menciona en esta página :

La declaración que usa el espacio de nombres estándar generalmente se considera una mala práctica. La alternativa a esta declaración es especificar el espacio de nombres al que pertenece el identificador utilizando el operador de ámbito (: :) cada vez que declaramos un tipo.

Y mira esta opinión :

No hay ningún problema al usar "usando el espacio de nombres estándar" en su archivo fuente cuando hace un uso intensivo del espacio de nombres y sabe con seguridad que nada colisionará.

Algunas personas han dicho que es una mala práctica incluir using namespace stden sus archivos fuente porque está invocando desde ese espacio de nombres todas las funciones y variables. Cuando desee definir una nueva función con el mismo nombre que otra función contenida en la namespace std, sobrecargaría la función y podría producir problemas debido a la compilación o ejecución. No se compilará ni ejecutará como espera.

Como se menciona en esta página :

Aunque la declaración nos evita escribir std :: siempre que deseamos acceder a una clase o tipo definido en el espacio de nombres estándar, importa la totalidad del espacio de nombres estándar al espacio de nombres actual del programa. Tomemos algunos ejemplos para entender por qué esto podría no ser tan bueno

...

Ahora, en una etapa posterior de desarrollo, deseamos usar otra versión de cout que se implemente de manera personalizada en alguna biblioteca llamada "foo" (por ejemplo)

...

Observe cómo hay una ambigüedad, ¿a qué biblioteca apunta cout? El compilador puede detectar esto y no compilar el programa. En el peor de los casos, el programa aún puede compilar pero llamar a la función incorrecta, ya que nunca especificamos a qué espacio de nombres pertenecía el identificador.


7

No creo que sea necesariamente una mala práctica en todas las condiciones, pero debes tener cuidado cuando la uses. Si está escribiendo una biblioteca, probablemente debería usar los operadores de resolución de alcance con el espacio de nombres para evitar que su biblioteca choque con otras bibliotecas. Para el código de nivel de aplicación, no veo nada de malo en ello.


7

"¿Por qué está 'usando el espacio de nombres std;' considerado una mala práctica en C ++? "

Lo digo al revés: ¿por qué escribir cinco caracteres adicionales es engorroso para algunos?

Considere, por ejemplo, escribir una pieza de software numérico. ¿Por qué incluso consideraría contaminar mi espacio de nombres global cortando "std :: vector" general a "vector" cuando "vector" es uno de los conceptos más importantes del dominio del problema?


19
No son solo 5 caracteres adicionales; sus 5 caracteres adicionales cada vez que hace referencia a cualquier tipo de objeto en la biblioteca estándar. Lo cual, si está utilizando mucho la biblioteca estándar, será frecuente. Por lo tanto, son más realistas miles de caracteres adicionales en un programa de tamaño decente. Presumiblemente, la directiva 'usar' se agregó al lenguaje para poder usarla ...
Jeremy Friesner

55
No son 5 caracteres adicionales cada vez, son 5 caracteres y probablemente un par de clics del mouse para desplegar un menú y buscar y reemplazar en el editor de su elección.
DaveWalley

1
Legibilidad. cout << hex << setw(4) << i << endl;es más fácil de leer questd::cout << std::hex << std::setw(4) << i << std::endl;
oz1cz

16
Y aún peor: std::map<std::string,std::pair<std::string,std::string>>es horrible en comparación con map<string,pair<string,string>>.
oz1cz

44
Es una buena práctica escribir de todos modos sus contenedores STL para que std :: realmente no importe. Y C ++ 11 nos trajo la palabra clave auto que hace las cosas aún más fáciles cuando, por ejemplo, usamos iteradores.
juzzlin

7

Estoy de acuerdo con los demás: está pidiendo conflictos de nombres, ambigüedades y el hecho es que es menos explícito. Si bien puedo ver el uso deusing , mi preferencia personal es limitarlo. También consideraría fuertemente lo que otros señalaron:

Si desea encontrar un nombre de función que podría ser un nombre bastante común, pero solo desea encontrarlo en el stdespacio de nombres (o al revés, desea cambiar todas las llamadas que no están en el espacio de nombres std, espacio de nombres X, ...), entonces, ¿cómo propones hacer esto?

Podrías escribir un programa para hacerlo, pero ¿no sería mejor pasar tiempo trabajando en tu proyecto en lugar de escribir un programa para mantenerlo?

Personalmente, en realidad no me importa el std::prefijo. Me gusta más el aspecto que no tenerlo. No sé si eso es porque es explícito y me dice "este no es mi código ... estoy usando la biblioteca estándar" o si es otra cosa, pero creo que se ve mejor. Esto podría ser extraño dado que recientemente ingresé a C ++ (usé y sigo haciendo C y otros lenguajes durante mucho más tiempo y C es mi lenguaje favorito de todos los tiempos, justo encima del ensamblaje).

Hay otra cosa, aunque está algo relacionada con lo anterior y lo que otros señalan. Si bien esto podría ser una mala práctica, a veces me reservo std::namepara la versión estándar de la biblioteca y el nombre para la implementación específica del programa. Sí, de hecho, esto podría morderte y morderte duro, pero todo se reduce a que comencé este proyecto desde cero, y soy el único programador para ello. Ejemplo: lo sobrecargo std::stringy lo llamo string. Tengo adiciones útiles. Lo hice en parte debido a mi tendencia de C y Unix (+ Linux) hacia los nombres en minúsculas.

Además de eso, puede tener alias de espacio de nombres. Aquí hay un ejemplo de dónde es útil que podría no haber sido mencionado. Yo uso el estándar C ++ 11 y específicamente con libstdc ++. Bueno, no tiene std::regexsoporte completo . Claro, se compila, pero arroja una excepción en el sentido de que es un error al final del programador. Pero es falta de implementación.

Así que así es como lo resolví. Instale la expresión regular de Boost y vincúlela. Luego, hago lo siguiente para que cuando libstdc ++ lo haya implementado por completo, solo necesito eliminar este bloque y el código permanece igual:

namespace std
{
    using boost::regex;
    using boost::regex_error;
    using boost::regex_replace;
    using boost::regex_search;
    using boost::regex_match;
    using boost::smatch;
    namespace regex_constants = boost::regex_constants;
}

No discutiré si es una mala idea o no. Sin embargo, argumentaré que lo mantiene limpio para mi proyecto y al mismo tiempo lo hace específico: es cierto, tengo que usar Boost, pero lo estoy utilizando como libstdc ++ eventualmente lo tendrá. Sí, comenzar su propio proyecto y comenzar con un estándar (...) al principio ayuda mucho al mantenimiento, el desarrollo y todo lo relacionado con el proyecto.

Solo para aclarar algo: en realidad no creo que sea una buena idea usar el nombre de una clase / lo que sea en el STL deliberadamente y más específicamente en lugar de. La cadena es la excepción (ignore el primero, el anterior o el segundo aquí, haga un juego de palabras si es necesario) para mí, ya que no me gustó la idea de 'String'.

Tal como están las cosas, todavía estoy muy sesgado hacia C y hacia C ++. Ahorrando detalles, gran parte de lo que trabajo encaja más en C (pero fue un buen ejercicio y una buena manera de hacerme a. Aprender otro idioma yb. Tratar de no estar menos predispuesto contra el objeto / clases / etc., lo que tal vez sea mejor dicho como menos cerrado de mente, menos arrogante y más tolerante). Pero lo que es útil es lo que algunos ya sugirieron: de hecho uso la lista (es bastante genérica, ¿no es así?), Y clasifico (lo mismo) para nombrar dos que causarían un choque de nombres si lo hiciera using namespace std;, y así para ello prefiero ser específico, tener el control y saber que si pretendo que sea el uso estándar, tendré que especificarlo. En pocas palabras: no se permite suponer.

Y en cuanto a hacer parte de la expresión regular de Boost std. Lo hago para una futura integración y, una vez más, admito que esto es parcial, no creo que sea tan feo como boost::regex:: .... De hecho, eso es otra cosa para mí. Hay muchas cosas en C ++ que todavía tengo que aceptar completamente en miradas y métodos (otro ejemplo: plantillas variadas versus argumentos var [¡aunque admito que las plantillas variadas son muy, muy útiles!]). Incluso aquellos que sí acepto fueron difíciles, y todavía tengo problemas con ellos.



7

Según mi experiencia, si tiene varias bibliotecas que usan say cout, pero para un propósito diferente, puede usar el incorrecto cout.

Por ejemplo, si escribo en, using namespace std;y using namespace otherlib;y el tipo simplemente cout(que pasa a ser en ambos), en lugar de std::cout(o 'otherlib::cout'), es posible utilizar el equivocado, y obtener errores. Es mucho más efectivo y eficiente de usar std::cout.


6

Con los identificadores importados no calificados, necesita herramientas de búsqueda externas como grep para averiguar dónde se declaran los identificadores. Esto dificulta el razonamiento sobre la corrección del programa.


6

Depende de dónde se encuentre. Si es un encabezado común, está disminuyendo el valor del espacio de nombres al fusionarlo en el espacio de nombres global. Tenga en cuenta que esta podría ser una buena forma de hacer que los módulos sean globales.


6

Esta es una mala práctica, a menudo conocida como contaminación global del espacio de nombres. Pueden surgir problemas cuando más de un espacio de nombres tiene el mismo nombre de función con firma, entonces será ambiguo que el compilador decida a cuál llamar y todo esto se puede evitar cuando se especifica el espacio de nombres con su llamada de función como std::cout. Espero que esto ayude. :)


5

Para responder a su pregunta, lo veo prácticamente de esta manera: muchos programadores (no todos) invocan el espacio de nombres estándar. Por lo tanto, uno debe tener la costumbre de NO usar cosas que afecten o usen los mismos nombres que los que están en el espacio de nombres estándar. Es un gran acuerdo, pero no tanto en comparación con el número de posibles palabras coherentes y seudónimos que pueden surgir estrictamente hablando.

Quiero decir realmente ... decir "no confíes en que este esté presente" es solo configurarte para que confíes en que NO esté presente. Siempre tendrá problemas para tomar prestados fragmentos de código y repararlos constantemente. Simplemente mantenga sus cosas prestadas y definidas por el usuario en un alcance limitado como deberían ser y sea MUY respetuoso con los globales (honestamente, los globales casi siempre deberían ser el último recurso para los propósitos de "compilar ahora, cordura después"). Verdaderamente creo que es un mal consejo de tu maestro porque usar std funcionará tanto para "cout" como para "std :: cout" pero NO usar std solo funcionará para "std :: cout". No siempre tendrá la suerte de escribir todo su propio código.

NOTA: No se concentre demasiado en los problemas de eficiencia hasta que realmente aprenda un poco sobre cómo funcionan los compiladores. Con un poco de experiencia en la codificación, no tiene que aprender mucho sobre ellos antes de darse cuenta de cuánto pueden generalizar un buen código en algo simple. Tan simple como si escribieras todo en C. Un buen código es tan complejo como debe ser.


Dada la cantidad de personas que parecen ignorar las funciones útiles de la biblioteca estándar ( <algorithm>por ejemplo, reinventar cosas ), parece un poco exagerado imaginar que las mismas personas puedan evitar esos identificadores de manera confiable. Mire su propio código y dígame que nunca tiene una variable o función llamada count. O distance, o log, destroy, launch, visit, beta, sample, messages, clamp, erase, copy, modulus, left, etc. Por no hablar de todos los identificadores aún no está en stdque romperá su código cuando C ++ 35 sale ...
Toby Speight
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.