Espacios de nombres anónimos / anónimos vs. funciones estáticas

Una característica de C ++ es la capacidad de crear espacios de nombres sin nombre (anónimos), como los siguientes:

namespace { int cannotAccessOutsideThisFile() { ... } } // namespace 

Usted pensaría que tal característica sería inútil, ya que no puede especificar el nombre del espacio de nombres, es imposible acceder a cualquier elemento dentro de él desde el exterior. Pero estos espacios de nombre sin nombre son accesibles dentro del archivo en el que están creados, como si tuvieras una cláusula de uso implícita.

Mi pregunta es, ¿por qué o cuándo sería preferible usar funciones estáticas? ¿O son esencialmente dos formas de hacer exactamente lo mismo?

El estándar C ++ lee en la sección 7.3.1.1 Espacios de nombre sin nombre, párrafo 2:

El uso de la palabra clave estática se desaprobaba al declarar objetos en un ámbito de espacio de nombres, el espacio de nombres sin nombre proporciona una alternativa superior.

Static solo se aplica a nombres de objetos, funciones y uniones anónimas, no a declaraciones de tipos.

Editar:

La decisión de desaprobar este uso de la palabra clave estática (afecta la visibilidad de una statement de variable en una unidad de traducción) se ha invertido ( ref ). En este caso, utilizar un espacio de nombre estático o sin nombre vuelve a ser esencialmente dos formas de hacer exactamente lo mismo. Para más discusión, vea esta pregunta SO.

Los espacios de nombre sin nombre aún tienen la ventaja de permitirle definir tipos de unidad de traducción local. Por favor vea esta pregunta SO para más detalles.

El crédito va a Mike Percy por traer esto a mi atención.

Poner métodos en un espacio de nombre anónimo evita que infrinja accidentalmente la regla de una sola definición , lo que le permite no preocuparse nunca de nombrar sus métodos de ayuda al igual que con cualquier otro método con el que pueda vincular.

Y, como señaló luke, los espacios de nombres anónimos son preferidos por el estándar sobre los miembros estáticos.

Hay un caso límite en el que la estática tiene un efecto sorprendente (al menos para mí). El estándar C ++ 03 establece en 14.6.4.2/1:

Para una llamada de función que depende de un parámetro de plantilla, si el nombre de la función es una identificación no calificada pero no una plantilla-identificación , las funciones candidatas se encuentran usando las reglas de búsqueda habituales (3.4.1, 3.4.2) excepto que:

  • Para la parte de la búsqueda que utiliza la búsqueda de nombres no calificados (3.4.1), solo se encuentran declaraciones de funciones con enlaces externos desde el contexto de definición de la plantilla.
  • Para la parte de la búsqueda que usa espacios de nombres asociados (3.4.2), solo se encuentran declaraciones de funciones con enlaces externos encontrados en el contexto de definición de plantilla o en el contexto de instanciación de plantilla.

El siguiente código llamará a foo(void*) y no a foo(S const &) como es de esperar.

 template  int b1 (T const & t) { foo(t); } namespace NS { namespace { struct S { public: operator void * () const; }; void foo (void*); static void foo (S const &); // Not considered 14.6.4.2(b1) } } void b2() { NS::S s; b1 (s); } 

En sí mismo, probablemente este no sea un gran problema, pero destaca que para un comstackdor C ++ totalmente compatible (es decir, uno con soporte para export ), la palabra clave static todavía tendrá una funcionalidad que no está disponible de ninguna otra manera.

 // bar.h export template  int b1 (T const & t); // bar.cc #include "bar.h" template  int b1 (T const & t) { foo(t); } // foo.cc #include "bar.h" namespace NS { namespace { struct S { }; void foo (S const & s); // Will be found by different TU 'bar.cc' } } void b2() { NS::S s; b1 (s); } 

La única manera de garantizar que la función en nuestro espacio de nombres sin nombre no se encuentre en plantillas que usan ADL es hacerlo static .

Actualización para C ++ moderno

A partir de C ++ ’11, los miembros de un espacio de nombre sin nombre tienen un enlace interno implícitamente (3.5 / 4):

Un espacio de nombres sin nombre o un espacio de nombres declarado directa o indirectamente dentro de un espacio de nombres sin nombre tiene un enlace interno.

Pero, al mismo tiempo, se actualizó 14.6.4.2/1 para eliminar la mención de vinculación (esto tomado de C ++ ’14):

Para una llamada a función donde postfix-expression es un nombre dependiente, las funciones candidatas se encuentran utilizando las reglas de búsqueda habituales (3.4.1, 3.4.2), excepto que:

  • Para la parte de la búsqueda que utiliza la búsqueda de nombres no calificados (3.4.1), solo se encuentran las declaraciones de funciones del contexto de definición de la plantilla.

  • Para la parte de la búsqueda que utiliza espacios de nombres asociados (3.4.2), solo se encuentran las declaraciones de funciones que se encuentran en el contexto de definición de plantilla o en el contexto de instanciación de plantilla.

El resultado es que esta diferencia particular entre los miembros del espacio de nombres estáticos y sin nombre ya no existe.

Recientemente comencé a reemplazar palabras clave estáticas con espacios de nombres anónimos en mi código, pero de inmediato me encontré con un problema donde las variables en el espacio de nombres ya no estaban disponibles para su inspección en mi depurador. Estaba usando VC60, así que no sé si eso no es un problema con otros depuradores. Mi solución fue definir un espacio de nombres ‘módulo’, donde le di el nombre de mi archivo cpp.

Por ejemplo, en mi archivo XmlUtil.cpp, defino un espacio de nombres XmlUtil_I {…} para todas las variables y funciones de mi módulo. De esa forma puedo aplicar la calificación XmlUtil_I :: en el depurador para acceder a las variables. En este caso, el ‘_I’ lo distingue de un espacio de nombres público como XmlUtil que deseo usar en otro lugar.

Supongo que una desventaja potencial de este enfoque en comparación con uno realmente anónimo es que alguien podría violar el scope estático deseado mediante el uso del calificador del espacio de nombres en otros módulos. No sé si eso es una gran preocupación.

El uso de la palabra clave estática para ese propósito está en desuso por el estándar C ++ 98. El problema con la estática es que no se aplica a la definición de tipo. También es una palabra clave sobrecargada utilizada de diferentes maneras en diferentes contextos, por lo que los espacios de nombre sin nombre simplifican un poco las cosas.

Por experiencia, solo señalaré que, si bien es la forma en C ++ de colocar funciones anteriormente estáticas en el espacio de nombres anónimo, los comstackdores más antiguos a veces pueden tener problemas con esto. Actualmente, trabajo con unos pocos comstackdores para nuestras plataformas de destino, y el comstackdor de Linux más moderno está de acuerdo con colocar funciones en el espacio de nombres anónimo.

Pero un comstackdor anterior que se ejecuta en Solaris, con el que estamos casados ​​hasta una versión futura no especificada, a veces lo aceptará, y otras veces lo señalará como un error. El error no es lo que me preocupa, es lo que podría estar haciendo cuando lo acepta . De modo que, hasta que adoptemos una actitud moderna en todos los ámbitos, seguimos usando funciones estáticas (normalmente de ámbito de clase) en las que preferiríamos el espacio de nombres anónimo.

Además, si uno usa palabra clave estática en una variable como este ejemplo:

 namespace { static int flag; } 

No se vería en el archivo de mapeo

Habiendo aprendido de esta característica solo ahora mientras leía su pregunta, solo puedo especular. Esto parece proporcionar varias ventajas sobre una variable estática de nivel de archivo:

  • Los espacios de nombres anónimos se pueden anidar unos dentro de otros, proporcionando múltiples niveles de protección de los cuales los símbolos no pueden escapar.
  • Se podrían colocar varios espacios de nombres anónimos en el mismo archivo fuente, creando en efecto diferentes ámbitos de nivel estático dentro del mismo archivo.

Me interesaría saber si alguien ha usado espacios de nombres anónimos en código real.

Se puede ver una diferencia específica del comstackdor entre espacios de nombres anónimos y funciones estáticas comstackndo el siguiente código.

 #include  namespace { void unreferenced() { std::cout << "Unreferenced"; } void referenced() { std::cout << "Referenced"; } } static void static_unreferenced() { std::cout << "Unreferenced"; } static void static_referenced() { std::cout << "Referenced"; } int main() { referenced(); static_referenced(); return 0; } 

Comstackndo este código con VS 2017 (especificando el indicador de advertencia de nivel 4 / W4 para habilitar la advertencia C4505: se ha eliminado la función local sin referencia ) y gcc 4.9 con la función -Wunused o -Wall muestra que VS 2017 solo producirá una advertencia para la función estática no utilizada. gcc 4.9 y superior, así como también clang 3.3 y superior, producirá advertencias para la función sin referencia en el espacio de nombres y también una advertencia para la función estática no utilizada.

Demostración en vivo de gcc 4.9 y MSVC 2017

Personalmente prefiero funciones estáticas sobre espacios de nombres sin nombre por los siguientes motivos:

  • Es obvio y claro solo a partir de la definición de la función que es privada para la unidad de traducción donde se comstack. Con el espacio de nombres sin nombre, es posible que deba desplazarse y buscar para ver si una función se encuentra en un espacio de nombres.

  • las funciones en espacios de nombres pueden ser tratadas como externas por algunos comstackdores (más antiguos). En VS2017 todavía son externos. Por esta razón, incluso si una función está en un espacio de nombre sin nombre, es posible que desee marcarlos estáticos.

  • las funciones estáticas se comportan de forma muy similar en C o C ++, mientras que los espacios de nombres sin nombre son obviamente solo C ++. espacios de nombres sin nombre también agregan un nivel extra si la sangría y no me gusta eso 🙂

Por lo tanto, estoy feliz de ver que el uso de static para funciones ya no está en desuso .