¿Por qué debería preferir espacios de nombres sin nombre a funciones estáticas?
Una característica de C++ es la capacidad de crear espacios de nombres sin nombre (anónimos), así:
namespace {
int cannotAccessOutsideThisFile() { ... }
} // namespace
Se podría pensar que una característica así sería inútil: dado que no se puede especificar el nombre del espacio de nombres, es imposible acceder a nada dentro de él desde el exterior. Pero se puede acceder a estos espacios de nombres sin nombre dentro del archivo en el que se crearon, como si tuviera una cláusula de uso implícita para ellos.
Mi pregunta es, ¿por qué o cuándo sería preferible esto al uso de 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 nombres sin nombre, párrafo 2:
El uso de la palabra clave estática está obsoleto al declarar objetos en el ámbito de un espacio de nombres; el espacio de nombres sin nombre proporciona una alternativa superior.
Estático solo se aplica a nombres de objetos, funciones y uniones anónimas, no a declaraciones de tipo.
Editar:
La decisión de desaprobar este uso de la static
palabra clave (que afecta la visibilidad de una declaración de variable en una unidad de traducción) se ha revertido ( ref ). En este caso, usar a static
o an unnamed namespace
vuelve a ser esencialmente dos formas de hacer exactamente lo mismo. Para obtener más información, consulte esta pregunta SO.
Los Unnamed namespace
todavía tienen la ventaja de permitirle definir tipos de unidad de traducción local. Consulte esta pregunta SO para obtener más detalles.
El crédito es para Mike Percy por informarme sobre esto.
Poner métodos en un espacio de nombres anónimo evita que viole accidentalmente la regla de una definición , lo que le permite no preocuparse nunca por nombrar sus métodos auxiliares de la misma manera que otros métodos a los que puede vincular.
Y, como señaló Luke , el estándar prefiere los espacios de nombres anónimos a los miembros estáticos.
Hay un caso extremo en el que la estática tiene un efecto sorprendente (al menos lo fue 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 un ID no calificado pero no un ID de plantilla , 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 enlace externo desde el 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 declaraciones de funciones con enlaces externos que se encuentran en el contexto de definición de plantilla o en el contexto de creación de instancias de plantilla.
...
El siguiente código llamará foo(void*)
y no foo(S const &)
como cabría esperar.
template <typename T>
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, esto probablemente no sea gran cosa, pero resalta que para un compilador de C++ totalmente compatible (es decir, uno con soporte para export
), la static
palabra clave seguirá teniendo una funcionalidad que no está disponible de ninguna otra manera.
// bar.h
export template <typename T>
int b1 (T const & t);
// bar.cc
#include "bar.h"
template <typename T>
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 forma de garantizar que la función en nuestro espacio de nombres sin nombre no se encuentre en las plantillas que usan ADL es crearla static
.
Actualización para C++ moderno
A partir de C++ '11, los miembros de un espacio de nombres sin nombre tienen un vínculo interno implícito (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 vínculo 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 de función donde la expresión postfija es un nombre dependiente, 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 del contexto de definición de 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 creación de instancias de plantilla.
El resultado es que esta diferencia particular entre miembros de espacio de nombres estáticos y sin nombre ya no existe.
Personalmente prefiero las funciones estáticas a los espacios de nombres sin nombre por las siguientes razones:
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 compila. Con un espacio de nombres sin nombre, es posible que necesites desplazarte y buscar para ver si una función está en un espacio de nombres.
Algunos compiladores (más antiguos) pueden tratar las funciones en los espacios de nombres como externas. En VS2017 siguen siendo externos. Por esta razón, incluso si una función está en un espacio de nombres sin nombre, es posible que quieras marcarla como estática.
Las funciones estáticas se comportan de manera muy similar en C o C++, mientras que los espacios de nombres sin nombre son obviamente solo de C++. los espacios de nombres sin nombre también agregan un nivel adicional en la sangría y eso no me gusta :)
Por lo tanto, me alegra ver que el uso de funciones estáticas ya no está en desuso .