c++ use - ¿Por qué se considera una mala práctica el uso de "namespace std"?




que significa (25)

Otros me han dicho que escribir using namespace std en el código es incorrecto, y que debería usar std::cout y std::cin directamente en su lugar.

¿Por qué el using namespace std 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 std nombres std )? ¿Afecta el rendimiento?


Answers

Depende de donde se encuentre. Si es un encabezado común, entonces está disminuyendo el valor del espacio de nombres fusionándolo en el espacio de nombres global. Tenga en cuenta que esta podría ser una buena forma de crear módulos globales.


No se debe usar la directiva de uso a nivel 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 la búsqueda dependiente de argumentos).


Si importa los archivos del encabezado derecho, repentinamente tendrá nombres como hex , left , plus o count en su ámbito global. Esto puede ser sorprendente si no sabe que std:: contiene estos nombres. Si también trata de usar estos nombres localmente, puede causar bastante confusión.

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


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

using namespace std;
using namespace boost;

Muchas de las características de Boost están incorporadas 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, evitando using namespace X; es una forma de prueba de futuro, una forma de asegurarse de que un cambio en las bibliotecas y / o archivos de encabezado en uso no va a romper un programa.


No lo uses globalmente.

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

  • Usted desordena el espacio de nombres que está programando.
  • Los lectores tendrán dificultades para ver de dónde proviene un identificador particular, cuando usa muchos using namespace xyz .
  • Lo que sea cierto para otros lectores de su código fuente es aún más cierto para el lector más frecuente: usted mismo. Vuelve en uno o dos años y echa un vistazo ...
  • Si solo habla sobre el using namespace std es posible que no esté al tanto de todas las cosas que toma, y ​​cuando agrega otro #include o se muda a una nueva revisión de C ++, puede que tenga conflictos de nombre de los que no estaba al tanto.

Puedes usarlo localmente

Adelante y utilízalo localmente (casi) libremente. Esto, por supuesto, le impide la repetición de std:: - 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 función de swap para tus clases. Se sugirió que realmente use 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á std::swap para value_ , es decir, void std::swap(int, int) .
  • Si tiene una sobrecarga void swap(Child&, Child&) implementado, el compilador lo elegirá.
  • Si no tiene esa sobrecarga, el compilador utilizará void std::swap(Child&,Child&) e intentará intercambiar estos.

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


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

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


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 mejor opción para algunas de sus llamadas a Quux() que la bar::Quux() su código llamó por años. Entonces su código aún se compila , pero silenciosamente llama a la función incorrecta y sabe qué sabe Dios. Eso es tan malo como las cosas pueden llegar.

Tenga en cuenta que el std nombres std tiene toneladas de identificadores, muchos de los cuales son muy comunes ( list reflexión, sort , string , iterator , etc.) que probablemente también aparezcan en otro código.

Si considera que esto es poco probable: hubo una pregunta aquí en en la que casi exactamente sucedió esto (función incorrecta llamada debido a que se omite el prefijo std:: ) aproximadamente medio año después de dar esta respuesta. Here hay otro ejemplo más reciente de tal pregunta. Así que 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 directivas como las declaraciones están prohibidas, excepto para los ámbitos de funciones. ¿Adivina qué? A la mayoría de nosotros nos llevó muy pocas semanas acostumbrarnos a escribir el prefijo, y después de unas pocas semanas, la mayoría de nosotros incluso estuvimos de acuerdo en que hacía que el código fuera más legible . Hay una razón para eso: si te gusta la prosa más corta o más larga es subjetivo, 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 refiere.

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 sentí curiosidad por la frecuencia using que se utilizó el alcance de la función (permitida) en el proyecto. Le pregunté por las fuentes y solo encontré una o dos docenas de lugares donde se usaba. Para mí, esto indica que, una vez que lo intentamos, a los desarrolladores no les resulta lo suficientemente estd std:: suficientemente doloroso como para emplear el uso de directivas, incluso una vez cada 100 kLoC, incluso cuando se permitió su uso.

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


Desde mi experiencia, si tiene varias bibliotecas que usan, por ejemplo cout, pero para un propósito diferente puede usar el mal 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 eficaz y eficiente de usar std::cout.


"¿Por qué es 'utilizando espacio de nombres estándar;' ¿Se considera una mala práctica en C ++? "

Lo puse al revés: ¿por qué algunos tipos de caracteres adicionales se consideran engorrosos?

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


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 entradas y otras importaciones, tendrá un montón de dependencias para resolver el problema. Lo peor de esto 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 los archivos de cabecera o el espacio de nombres global. Probablemente esté bien hacerlo en ámbitos muy limitados, pero nunca he tenido problemas para escribir los 5 caracteres adicionales para aclarar de dónde provienen mis funciones.


Otra razón es la sorpresa.

Si veo cout << blah , en lugar de std::cout << blah

Creo que ¿qué es esto cout ? ¿Es el cout normal? ¿Es algo especial?


Versión corta: no use declaraciones 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 de C ++ (en negrita para enfatizar 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 de espacio de nombres usando directivas o declaraciones; en su lugar, explícitamente espacios de nombres-calificar todos los nombres. (La segunda regla se sigue de la primera, porque los encabezados nunca pueden saber qué otros encabezados #incluye podrían 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 repetidas afirmaciones de lo contrario, el espacio de nombres que utiliza declaraciones y directivas no es malo y no anula el propósito de los espacios de nombres. Más bien, son los que hacen que los espacios de nombres sean utilizables .


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

Normalmente 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 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;
}

Para responder a tu pregunta, lo veo de esta manera en la práctica: muchos programadores (no todos) invocan el espacio de nombres std. Por lo tanto, uno debe tener el hábito de NO usar cosas que incidan o usen los mismos nombres que están en el espacio de nombres estándar. Se trata de un gran acuerdo, pero no tanto en comparación con el número de posibles palabras coherentes y seudónimos que se pueden encontrar estrictamente.

Quiero decir realmente ... decir "no confíes en que esté presente" es solo hacer que confíes en que NO esté presente. Siempre vas a tener problemas para pedir fragmentos de código y repararlos constantemente. Simplemente mantenga sus cosas definidas por el usuario y prestadas en un alcance limitado como deberían y con MUCHO ahorro de globales (honestamente, los globales deberían ser siempre un último recurso para los propósitos de "compilar ahora, cordura más adelante"). Realmente creo que es un mal consejo de tu profesor porque usar std funcionará tanto para "cout" como para "std :: cout" pero NO usar std solo funcionará para "std :: cout". No siempre tendrás la suerte de escribir todo tu propio código.

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


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

using namespace foo;
using namespace bar;

Todo funciona bien, puedes llamar a 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: tanto Foo 2.0 como Bar importan Quux() en tu espacio de nombres global. Esto requerirá un poco de 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() no habría sido un evento.


Estoy de acuerdo en que no se debe usar globalmente, pero no es tan malo utilizarlo localmente, como en un 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 derivadas de su composición.

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


Es bueno ver el código y saber lo que hace. Si veo std::cout , sé que esa es la secuencia de cout de la biblioteca std . Si veo cout entonces no lo sé. Podría ser la secuencia cout de la biblioteca std . O podría haber un int cout = 0; Diez líneas más altas en la misma función. O una variable static llamada cout en ese archivo. Podría ser cualquier cosa.

Ahora tome un código base 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 cout static int llamado static int , desplazarlo hacia la izquierda un bit y tirar el resultado. Buscando un error, tendría que comprobar eso. ¿Puedes ver cómo realmente prefiero ver std::cout ?

Es una de estas cosas que parece una muy buena idea si usted es profesor y nunca tuvo que escribir y mantener ningún código para ganarse la vida. 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.


Un espacio de nombres es un ámbito con nombre. Los espacios de nombres se utilizan para agrupar declaraciones relacionadas y para mantener separados los elementos separados. Por ejemplo, dos bibliotecas desarrolladas por separado pueden usar el mismo nombre para referirse a diferentes elementos, pero un usuario todavía 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 los lectores como para los escritores. En consecuencia, es posible afirmar que los nombres de un espacio de nombres en particular están disponibles sin una 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 administración de diferentes bibliotecas y diferentes versiones de código. En particular, le ofrecen al programador alternativas de cómo hacer una referencia a un nombre no local.

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


El uso de muchos espacios de nombres al mismo tiempo es obviamente una receta para el desastre, pero usar JUST espacio de nombres std y solo espacio de nombres std no es tan importante en mi opinión porque la redefinición solo puede ocurrir por su propio código ...

Entonces, considérelas funciones como nombres reservados como "int" o "class" y eso es todo.

La gente debería dejar de ser tan anal al respecto. Tu profesor tenía razón todo el tiempo. Solo usa UN espacio de nombres; ese es el punto central de usar espacios de nombres en primer lugar. Se supone que no debes usar más de uno al mismo tiempo. A menos que sea el tuyo. Así que de nuevo, la redefinición no va a suceder.


Un ejemplo donde el uso de espacio de nombres arroja un error de compilación debido a la ambigüedad del conteo, 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;
}

Con identificadores importados no calificados, necesita herramientas de búsqueda externas como grep para averiguar dónde se declaran los identificadores. Esto hace que el razonamiento sobre la corrección del programa sea más difícil.


No creo que sea necesariamente una mala práctica en todas las condiciones, pero debe tener cuidado al usarla. 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 se tope con otras bibliotecas. Para el código de nivel de aplicación, no veo nada malo en ello.


Los programadores experimentados usan lo que resuelve sus problemas y evitan todo lo que crea nuevos problemas, y evitan las directivas de uso a nivel de archivo de cabecera 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 ? A veces, los programadores quieren desactivar ADL explícitamente, otras veces quieren desambiguar.

Así que lo siguiente está bien:

  1. Directivas de uso a nivel de función y declaraciones de uso dentro de las 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 fuente

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, así que no debería estropearlo con tirar todo en una bolsa global. Sin embargo, si uso a menudo 'cout' y 'cin', escribo: using std::cout; using std::cin; using std::cout; using std::cin; en el archivo cpp (nunca en el archivo de encabezado, ya que se propaga con #include ). Pienso que nadie cuerdo nombrará nunca una secuencia de cout o cin . ;)


Para C ++ depende de lo que estás haciendo. OK, es un código estúpido, pero imagina

class myTimeEatingClass
{
 public:
 //constructor
      myTimeEatingClass()
      {
          sleep(2000);
          ms_usedTime+=2;
      }
      ~myTimeEatingClass()
      {
          sleep(3000);
          ms_usedTime+=3;
      }
      const unsigned int getTime() const
      {
          return  ms_usedTime;
      }
      static unsigned int ms_usedTime;
};
myTimeEatingClass::ms_CreationTime=0; 
myFunc()
{
    for (int counter = 0; counter <= 10; counter++) {

        myTimeEatingClass timeEater();
        //do something
    }
    cout << "Creating class took "<< timeEater.getTime() <<"seconds at all<<endl;

}
myOtherFunc()
{
    myTimeEatingClass timeEater();
    for (int counter = 0; counter <= 10; counter++) {
        //do something
    }
    cout << "Creating class took "<< timeEater.getTime() <<"seconds at all<<endl;

}

Esperará 55 segundos hasta que obtenga el resultado de myFunc. Solo porque cada contructor de bucle y destructor juntos necesitan 5 segundos para terminar.

Necesitarás 5 segundos hasta que obtengas la salida de myOtherFunc.

Por supuesto, este es un ejemplo loco.

Pero ilustra que podría convertirse en un problema de rendimiento cuando cada ciclo se realiza la misma construcción cuando el constructor y / o destructor necesita algo de tiempo.





c++ namespaces std using-directives c++-faq