¿Para qué son los espacios de nombres en línea?

C ++ 11 permite inline namespace , todos los miembros también están automáticamente en el namespace adjunto. No puedo pensar en ninguna aplicación útil de esto. ¿Puede alguien dar un breve y sucinto ejemplo de una situación en la que se necesita un inline namespace y donde es la solución más idiomática?

(Además, no me queda claro qué sucede cuando un namespace se declara en inline en una, pero no en todas las declaraciones, que pueden residir en diferentes archivos. ¿No es esto un problema?)

Los espacios de nombres en línea son una función de control de versiones de bibliotecas similar a la creación de versiones de símbolos , pero implementadas puramente en el nivel C ++ 11 (es decir, multiplataforma) en lugar de ser una característica de un formato ejecutable binario específico (es decir, específico de la plataforma).

Es un mecanismo por el cual un autor de biblioteca puede hacer que un espacio de nombres nested parezca como si todas sus declaraciones estuvieran en el espacio de nombres circundante (los espacios de nombres en línea pueden anidarse, de modo que los nombres “más nesteds” se filtran hasta el primer -n espacio de nombres en línea y mira y actúa como si sus declaraciones estuvieran en cualquiera de los espacios de nombres intermedios, también).

Como ejemplo, considere la implementación de STL del vector . Si tuviéramos espacios de nombres en línea desde el comienzo de C ++, entonces en C ++ 98 el encabezado podría haber tenido este aspecto:

 namespace std { #if __cplusplus < 1997L // pre-standard C++ inline #endif namespace pre_cxx_1997 { template  __vector_impl; // implementation class template  // eg w/o allocator argument class vector : __vector_impl { // private inheritance // ... }; } #if __cplusplus >= 1997L // C++98/03 or later // (ifdef'ed out b/c it probably uses new language // features that a pre-C++98 compiler would choke on) # if __cplusplus == 1997L // C++98/03 inline # endif namespace cxx_1997 { // std::vector now has an allocator argument template  > class vector : pre_cxx_1997::__vector_impl { // the old impl is still good // ... }; // and vector is special: template  > class vector { // ... }; }; #endif // C++98/03 or later } // namespace std 

Dependiendo del valor de __cplusplus , se __cplusplus una u otra implementación del vector . Si su código base fue escrito en pre-C ++ 98 veces, y encuentra que la versión de C ++ 98 del vector está causando problemas cuando actualiza su comstackdor, “todo” lo que tiene que hacer es encontrar las referencias a std::vector en su base de código y reemplazarlos por std::pre_cxx_1997::vector .

Pase al siguiente estándar, y el proveedor de STL solo repite el procedimiento nuevamente, presentando un nuevo espacio de nombre para std::vector con soporte emplace_back (que requiere C ++ 11) y __cplusplus == 201103L ese iff __cplusplus == 201103L .

OK, entonces ¿por qué necesito una nueva función de idioma para esto? Ya puedo hacer lo siguiente para tener el mismo efecto, ¿no?

 namespace std { namespace pre_cxx_1997 { // ... } #if __cplusplus < 1997L // pre-standard C++ using namespace pre_cxx_1997; #endif #if __cplusplus >= 1997L // C++98/03 or later // (ifdef'ed out b/c it probably uses new language // features that a pre-C++98 compiler would choke on) namespace cxx_1997 { // ... }; # if __cplusplus == 1997L // C++98/03 using namespace cxx_1997; # endif #endif // C++98/03 or later } // namespace std 

Dependiendo del valor de __cplusplus , obtengo una u otra de las implementaciones.

Y estarías casi en lo cierto.

Considere el siguiente código de usuario válido de C ++ 98 (se le permitió especializar completamente las plantillas que ya viven en el espacio de nombres std en C ++ 98):

 // I don't trust my STL vendor to do this optimisation, so force these // specializations myself: namespace std { template <> class vector : my_special_vector { // ... }; template <> class vector : my_special_vector { // ... }; // ...etc... } // namespace std 

Este es un código perfectamente válido en el que el usuario proporciona su propia implementación de un vector para un conjunto de tipos en el que aparentemente conoce una implementación más eficiente que la que se encuentra en (su copia de) el STL.

Pero : cuando se especializa en una plantilla, debe hacerlo en el espacio de nombres en el que se declaró. El Estándar dice que el vector se declara en el espacio de nombres estándar, por lo que es donde el usuario espera especializar el tipo.

Este código funciona con un estándar de espacio de nombres no versionado, o con la característica de espacio de nombres en línea C ++ 11, pero no con el truco de control de versiones que usó el using namespace , porque expone los detalles de implementación que el espacio de nombres verdadero en el que se definido no fue std directamente.

Hay otros agujeros por los que puede detectar el espacio de nombres nested (ver comentarios a continuación), pero los espacios de nombres en línea los conectan a todos. Y eso es todo lo que hay que hacer. Inmensamente útil para el futuro, pero AFAIK the Standard no prescribe nombres de espacios de nombres en línea para su propia biblioteca estándar (me encantaría que se demuestre que está equivocado en esto, sin embargo), por lo que solo se puede usar para bibliotecas de terceros, no el estándar en sí (a menos que los proveedores del comstackdor acuerden un esquema de nombres).

http://www.stroustrup.com/C++11FAQ.html#inline-namespace (un documento escrito y mantenido por Bjarne Stroustrup, de quien usted pensaría que debería estar al tanto de la mayoría de las motivaciones para la mayoría de las características de C ++ 11. )

De acuerdo con eso, es para permitir la versión de compatibilidad hacia atrás. Usted define múltiples espacios de nombres internos, y hace que el más reciente en inline . O de todos modos, el predeterminado para las personas que no se preocupan por el control de versiones. Supongo que la más reciente podría ser una versión futura o de vanguardia que aún no está predeterminada.

El ejemplo dado es:

 // file V99.h: inline namespace V99 { void f(int); // does something better than the V98 version void f(double); // new feature // ... } // file V98.h: namespace V98 { void f(int); // does something // ... } // file Mine.h: namespace Mine { #include "V99.h" #include "V98.h" } #include "Mine.h" using namespace Mine; // ... V98::f(1); // old version V99::f(1); // new version f(1); // default version 

No veo de inmediato por qué no pones using namespace V99; dentro del espacio de nombres Mine , pero no tengo que entender completamente el caso de uso para tomar la palabra de Bjarne sobre la motivación del comité.