2010-04-01 11 views
11

algo que me ha estado molestando por un tiempo:¿Debería cada clase tener su propio espacio de nombres?

La visión actual es que los tipos deben mantenerse en un espacio de nombres que sólo contiene funciones que son parte de la interfaz que no sea miembro de la clase (ver C++ estándares de codificación Sutter y Alexandrescu o here) para evitar que ADL obtenga definiciones no relacionadas.

¿Esto implica que todas las clases deben tener un espacio de nombres propio? Si suponemos que una clase puede ser aumentada en el futuro mediante la adición de funciones no miembro, entonces nunca puede ser seguro poner dos tipos en el mismo espacio de nombres ya que cualquiera de ellos puede introducir funciones no miembro eso podría interferir con el otro.

La razón por la que pregunto es que los espacios de nombres se vuelven engorrosos para mí. Estoy escribiendo una biblioteca de solo cabecera y me encuentro usando nombres de clases como project :: component :: class_name :: class_name. Sus implementaciones llaman a las funciones auxiliares pero como estas no pueden estar en el mismo espacio de nombres, también tienen para ser totalmente calificadas.

Editar:

Varias respuestas han sugerido que los espacios de nombres de C++ son simplemente un mecanismo para evitar conflictos de nombres. Esto no es asi En C++, las funciones que toman un parámetro se resuelven utilizando Argument Dependent Lookup. Esto significa que cuando el compilador intenta encontrar una definición de función que coincida con el nombre de la función verá cada función en el mismo espacio de nombres como el tipo (s) de sus parámetros cuando encuentre candidatos.

Esto puede tener consecuencias no deseadas y desagradables como se detalla en A Modest Proposal: Fixing ADL. Los estados de la regla de Sutter y Alexandrescu nunca ponen una función en el mismo espacio de nombres que una clase a menos que se suponga que sea parte de la interfaz de esa clase. No veo cómo puedo obedecer esa regla a menos que esté dispuesto a dar a cada clase su propio espacio de nombres.

Más sugerencias muy bienvenidas!

+2

"Sus implementaciones llaman a las funciones auxiliares [que] tienen que ser completamente calificadas" - puede poner una declaración o directiva 'using' dentro de un cuerpo de función, si eso ayuda en absoluto. –

+4

Por el amor de todas las cosas sagradas, no aplique una proporción 1: 1 entre clases y espacios de nombres. Derrota todo el propósito de tener espacios de nombres, y lleva a un montón de trabajo adicional con los dedos. –

+3

@James D: No estoy de acuerdo. Creo que no va lo suficientemente lejos. También debemos asegurarnos de que cada espacio de nombre esté en un espacio de nombre. –

Respuesta

7

Para evitar ADL, solo necesita dos espacios de nombres: uno con todas sus clases y el otro con todas sus funciones sueltas. ADL definitivamente no es una buena razón para que cada clase tenga su propio espacio de nombres.

Ahora, si quiere que se encuentren algunas funciones a través de ADL, es posible que desee crear un espacio de nombres para tal fin. Pero aún es bastante improbable que necesite un espacio de nombres separado por clase para evitar colisiones ADL.

0

Probablemente no. Ver Eric Lippert's post on the subject.

par de cosas aquí:

  1. Eric Lippert es un diseñador # C, pero lo que está diciendo acerca de un mal diseño jerárquico se aplica aquí.
  2. Mucho de lo que se describe en ese artículo tiene que ver con nombrar a su clase lo mismo que un espacio de nombres en C#, pero muchas de las mismas trampas se aplican a C++.

Puede ahorrar en algunos de los dolores typedef usando typedef s pero eso es por supuesto solo una curita.

+1

El consejo de Eric Lippert es excelente para C#, y tal vez .Net en general, pero en realidad no se aplica a C++ porque los espacios de nombres C++ no funcionan igual. En este caso, se necesitan espacios de nombres C++ para evitar que las funciones * que no son de clase sean ambiguas. – Gabe

+0

Aún no debería tener nombres de clase y espacio de nombres iguales. Todavía te encuentras con el mismo tipo de problemas de resolución. Estoy de acuerdo con que las funciones no de clase son un tidbit que C# no tiene que tratar, pero C# tiene métodos de extensión con los que C++ no se ocupa tampoco, así que creo que son parejos incluso en ese departamento. En cualquier caso, estoy de acuerdo con usted en que hay mejores respuestas aquí. Pero no me desagrada lo suficiente como para borrarlo. –

15

No. Nunca escuché esa convención. Por lo general, cada biblioteca tiene su propio espacio de nombres, y si esa biblioteca tiene múltiples módulos diferentes (por ejemplo, diferentes unidades lógicas que difieren en funcionalidad), entonces podría tener con su propio espacio de nombres, aunque un espacio de nombres por biblioteca es suficiente.Dentro del espacio de nombre de la biblioteca o módulo, puede usar el espacio de nombres detail o un espacio de nombre anónimo para almacenar detalles de implementación. El uso de un espacio de nombres por clase es, en mi humilde opinión, completo overkill. Definitivamente me alejaría de eso. Al mismo tiempo, le recomiendo encarecidamente que tenga al menos un espacio de nombres para su biblioteca y que ponga todo dentro de ese espacio de nombres o un espacio de nombres secundario para evitar conflictos de nombres con otras bibliotecas.

Para hacer esto más concreto, permítanme usar el venerable Boost C++ Libraries como ejemplo. Todos los elementos dentro de boost residen en boost::. Hay algunos módulos dentro de Boost, como la biblioteca de interproceso que tiene su propio espacio de nombres, como boost::interprocess::, pero en su mayor parte, los elementos de refuerzo (especialmente los que se usan con mucha frecuencia y entre los módulos) simplemente residen en boost::. Si mira dentro de boost, con frecuencia usa boost::detail o boost::name_of_module::detail para almacenar detalles de implementación para el espacio de nombres dado. Te sugiero que modeles tus espacios de nombres de esa manera.

+0

+1 por "no"; sin embargo, no tendría uno por biblioteca; una biblioteca es una unidad de implementación. Un espacio de nombre suele tener que ver con equipos/proyectos/subproyectos. Podría tener un espacio de nombres que abarque 2 libs y un exe – pm100

+1

@ pm100, sí, si tiene un proyecto de varios módulos (uno con múltiples bibliotecas u otras unidades lógicas), entonces podría unificarlos colectivamente bajo un espacio de nombres, pero luego sugeriría subdividir internamente un espacio de nombres por biblioteca. –

8

¡No, no y miles de veces no! Los espacios de nombres en C++ no son elementos arquitectónicos o de diseño. Son simplemente un mecanismo para prevenir los conflictos de nombres. Si en la práctica no tiene conflictos de nombres, no necesita espacios de nombres.

+0

mil y una veces no –

+4

"Son simplemente un mecanismo para prevenir los conflictos de nombres". Falso. Los espacios de nombres también son para dirigir ADL. De todos modos, para lo que están "a favor" es mucho menos importante que lo que realmente hacen *, si lo que hacen es indeseable en algunos casos particulares. –

+2

Los espacios de nombres son precisamente * no * ¡un simple mecanismo de nomenclatura! Con Argument Dependent Lookup (http://en.wikipedia.org/wiki/Argument_dependent_name_lookup) cada función en el mismo espacio de nombres como clase se considera candidata cuando se utiliza un objeto de la clase. De ahí la regla de Sutter y Alexandrescu. – thehouse

0

Es un trabajo bastante interesante, pero dado los autores, había una buena posibilidad de que lo fuera. Sin embargo, observo que el problema tiene que ver sobre todo:

  • typedef, ya que sólo introducen un alias y no un nuevo tipo
  • plantillas

Si hago:

namespace foo 
{ 
    class Bar; 

    void copy(const Bar&, Bar&, std::string); 
} 

E invocarlo:

#include <algorithms> 

#include "foo/bar.h" 

int main(int argc, char* argv[]) 
{ 
    Bar source; Bar dest; 
    std::string parameter; 
    copy(source, dest, parameter); 
} 

Luego debe elegir foo::copy. De hecho, considerará tanto foo::copy como std::copy, pero foo::copy que no sea plantilla tendrá prioridad.

+0

El compilador elegirá una función genérica si es una mejor coincidencia que una no genérica. En su caso, está bien, pero no porque 'foo :: copy' no sea una plantilla, sino porque al permitir' std :: copy' resultaría una llamada ambigua. Si 'parameter' hubiera sido, por ejemplo, un literal de cadena, se habría elegido' std :: copy', porque puede aceptar dicho valor directamente, mientras que 'foo :: copy' requeriría una conversión. –

+0

En realidad, al hacer 'parameter' una cadena literal,' std :: copy' ya no se consideraría, por lo que aún está bien. Pero el punto general es. –

+0

Exacto, olvidé mencionar el punto acerca de las conversiones. –

Cuestiones relacionadas