Forma correcta de definir métodos de espacio de nombres C ++ en el archivo .cpp

108

Probablemente un duplicado, pero no fácil de buscar ...

Dado un encabezado como:

namespace ns1
{
 class MyClass
 {
  void method();
 };
}

Lo he visto method()definido de varias maneras en el archivo .cpp:

Versión 1:

namespace ns1
{
 void MyClass::method()
 {
  ...
 }
}

Versión 2:

using namespace ns1;

void MyClass::method()
{
 ...
}

Versión 3:

void ns1::MyClass::method()
{
 ...
}

¿Existe una forma "correcta" de hacerlo? ¿Alguno de estos es "incorrecto" en el sentido de que no todos significan lo mismo?

Señor chico
fuente
En la mayoría de los códigos, normalmente veo la tercera versión (pero no sé por qué: D), la segunda versión es simplemente opuesta a por qué se introducen los espacios de nombres, supongo.
Señor Anubis
1
Como dato interesante, las herramientas de refactorización de Visual Assist están felices de generar código usando el # 1 o el # 3, dependiendo del estilo que ya esté en uso en el archivo de destino.
Mr. Boy

Respuestas:

51

La versión 2 no es clara y no es fácil de entender porque no sabe qué espacio de nombres MyClass pertenece y es simplemente ilógico (¿la función de clase no está en el mismo espacio de nombres?)

La versión 1 es correcta porque muestra que en el espacio de nombres, está definiendo la función.

La versión 3 también es correcta porque usó el ::operador de resolución de alcance para hacer referencia a MyClass::method ()en el espacio de nombresns1 . Prefiero la versión 3.

Consulte Espacios de nombres (C ++) . Esta es la mejor forma de hacer esto.

GILGAMESH
fuente
22
Llamar al número 2 "incorrecto" es una enorme exageración. Según esta lógica, todos los nombres de símbolos son "incorrectos" porque potencialmente pueden ocultar otros nombres de símbolos en otros ámbitos.
diez
Es ilógico. Se compilará bien (lo siento, misex lo explicó en la respuesta), pero ¿por qué definiría una función fuera de su espacio de nombres? Confunde al lector. Además, cuando se utilizan muchos espacios de nombres, no muestra a qué espacio de nombres pertenece MyClass. Las versiones 1 y 3 solucionan este problema. En conclusión, no está mal, pero es poco claro y confuso.
GILGAMESH
3
Estoy de acuerdo con @PhoenicaMacia, el truco de uso es horrible y puede generar confusión. Considere una clase que implementa un operador como una función libre, en el encabezado que tendría namespace N {struct X { void f(); }; X operator==( X const &, X const & ); }, ahora en el archivo cpp con la instrucción using puede definir la función miembro como void X::f() {}, pero si define X operator==(X const&, X const&)estará definiendo un operador diferente al definido en el encabezado (tendrá que usar 1 o 3 para la función gratuita allí).
David Rodríguez - dribeas
1
En particular, prefiero 1, y el ejemplo en el artículo vinculado realmente no resuelve nada, el primer ejemplo usa 1, el segundo usa una combinación de 1 y 3 (las funciones se definen con calificación, pero se definen dentro del espacio de nombres externo)
David Rodríguez - dribeas
1
De los 3, diría que 1) es el mejor, sin embargo, la mayoría de los IDE tienen el hábito bastante molesto de sangrar todo dentro de esa declaración de espacio de nombres y agrega algo de confusión.
locka
28

5 años después y pensé en mencionar esto, que se ve bien y no es malo

using ns1::MyClass;

void MyClass::method()
{
  // ...
}
Puzomor Croacia
fuente
3
Esta es la mejor respuesta. Se ve más limpio y evita los problemas con las Versiones 1 de OP, que pueden traer cosas al espacio de nombres sin querer, y 2, que pueden traer cosas al espacio global sin querer.
ayane_m
Sí, esta es una gran combinación de escribir menos de 3, mientras que aún declara explícitamente la intención.
jb
14

Estoy usando la versión 4 (a continuación) porque combina la mayoría de las ventajas de la versión 1 (la concisión de la definición resoectiva) y la versión 3 (sea máximamente explícita). La principal desventaja es que la gente no está acostumbrada pero como lo considero técnicamente superior a las alternativas no me importa.

Versión 4: use la calificación completa usando alias de espacio de nombres:

#include "my-header.hpp"
namespace OI = outer::inner;
void OI::Obj::method() {
    ...
}

En mi mundo, con frecuencia uso alias de espacios de nombres ya que todo está calificado explícitamente, a menos que no pueda (por ejemplo, nombres de variables) o sea un punto de personalización conocido (por ejemplo, swap () en una plantilla de función).

Dietmar Kühl
fuente
1
Si bien creo que la lógica de "es mejor, así que no me importa si confunde a la gente" es defectuosa, estoy de acuerdo en que este es un buen enfoque para los espacios de nombres anidados.
Mr. Boy
1
+1 por la excelente idea de "por qué no pensé en eso". (En cuanto a "las personas no están acostumbradas a [cosas nuevas técnicamente superiores]", se acostumbrarán si más personas lo hacen).
wjl
Solo para asegurarme de que lo entiendo, ¿ya están ambos outery innerdefinidos como espacios de nombres en otros archivos de encabezado?
dekuShrub
4

La versión 3 hace que la asociación entre la clase y el espacio de nombres sea muy explícita a expensas de escribir más. La versión 1 evita esto pero captura la asociación con un bloque. La versión 2 tiende a ocultar esto, así que evitaría esa.

Paul Joireman
fuente
3

Elijo Num.3 (también conocida como la versión detallada). Es más mecanografiado, pero la intención es exacta para ti y para el compilador. El problema que publicaste tal cual es en realidad más simple que el mundo real. En el mundo real, existen otros ámbitos para las definiciones, no solo los miembros de la clase. Sus definiciones no son muy complicadas solo con clases, porque su alcance nunca se vuelve a abrir (a diferencia de los espacios de nombres, el alcance global, etc.).

Num.1, esto puede fallar con ámbitos que no sean clases, cualquier cosa que se pueda volver a abrir. Por lo tanto, puede declarar una nueva función en un espacio de nombres utilizando este enfoque, o sus líneas pueden terminar siendo sustituidas a través de ODR. Lo necesitará para algunas definiciones (en particular, especializaciones de plantillas).

Num.2 Esto es muy frágil, particularmente en bases de código grandes - a medida que cambian los encabezados y las dependencias, su programa no podrá compilarse.

Núm.3 Esto es ideal, pero hay mucho que escribir: cuál es su intención de definir algo . Esto hace exactamente eso, y el compilador se activa para asegurarse de que no haya cometido un error, una definición no esté desincronizada con su declaración, etc.

justin
fuente
2

Todos los caminos son correctos y cada uno tiene sus ventajas y desventajas.

En la versión 1, tiene la ventaja de no tener que escribir el espacio de nombres delante de cada función. La desventaja es que obtendrá una identificación aburrida, especialmente si tiene más de un nivel de espacios de nombres.

En la versión 2, hace que su código sea más limpio, pero si tiene más de un espacio de nombres implementado en el CPP, uno puede acceder a las funciones y variables del otro directamente, haciendo que su espacio de nombres sea inútil (para ese archivo cpp).

En la versión 3, tendrá que escribir más y sus líneas de función pueden ser más grandes que la pantalla, lo cual es malo para los efectos de diseño.

También hay otra forma en que algunas personas lo usan. Es similar a la primera versión, pero sin problemas de identificación.

Es como esto:

#define OPEN_NS1 namespace ns1 { 
#define CLOSE_NS1 }

OPEN_NS1

void MyClass::method()
{
...
}

CLOSE_NS1

Depende de usted elegir cuál es mejor para cada situación =]

Renan Greinert
fuente
14
No veo ninguna razón para usar una macro aquí. Si no quiere sangrar, simplemente no lo haga. Usar una macro solo hace que el código sea menos obvio.
diez
1
Creo que la última versión que mencionas es útil siempre que quieras compilar tu código con compiladores antiguos que no admiten espacios de nombres (Sí, todavía existen algunos dinosaurios). En ese caso, puede poner la macro dentro de una #ifdefcláusula.
Luca Martini
No tiene que identificarse si no lo desea, pero si no usa macros, algunos IDE intentarán hacerlo por usted. Por ejemplo, en Visual Studio puede seleccionar todo el código y presionar ALT + F8 para auto-identificarse. Si no usa las definiciones, perderá esa funcionalidad. Además, no creo que OPEN_ (espacio de nombres) y CLOSE_ (espacio de nombres) sean menos obvios, si lo tiene en su estándar de codificación. La razón que dio @LucaMartini también es interesante.
Renan Greinert
Si esto se hizo genérico, es decir #define OPEN_NS(X), creo que es un poco útil, pero no realmente ... No me opongo a las macros, pero esto parece un poco OTT. Creo que el enfoque de Dietmar Kühl es mejor para los espacios de nombres anidados.
Mr. Boy