c++ - que - Nombres/espacios de nombres anónimos vs. funciones estáticas
para que sirve el using namespace std en dev c++ (10)
Una característica de C ++ es la capacidad de crear espacios de nombres sin nombre (anónimos), como por ejemplo:
namespace {
int cannotAccessOutsideThisFile() { ... }
} // namespace
Usted pensaría que una característica de este tipo sería inútil, ya que no puede especificar el nombre del espacio de nombres, es imposible acceder a cualquier elemento desde dentro. Pero estos espacios de nombres sin nombre son accesibles dentro del archivo en el que se crearon, como si tuvieras una cláusula de uso implícita para ellos.
Mi pregunta es, ¿por qué o cuándo sería preferible utilizar funciones estáticas? ¿O son esencialmente dos formas de hacer exactamente lo mismo?
Además, si se usa una palabra clave estática en una variable como este ejemplo:
namespace {
static int flag;
}
No se vería en el archivo de mapeo.
Al enterarme de esta característica solo ahora mientras leo su pregunta, solo puedo especular. Esto parece proporcionar varias ventajas sobre una variable estática a nivel de archivo:
- Los espacios de nombres anónimos se pueden anidar entre sí, proporcionando múltiples niveles de protección de los que los símbolos no pueden escapar.
- Se podrían colocar varios espacios de nombres anónimos en el mismo archivo de origen, creando en efecto diferentes ámbitos de nivel estático dentro del mismo archivo.
Me interesaría saber si alguien ha utilizado espacios de nombres anónimos en código real.
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 nombres sin nombre simplifican un poco las cosas.
Hay un caso de borde donde 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 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 nombre no calificado (3.4.1), solo se encuentran las declaraciones de función 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 las declaraciones de funciones con vínculos externos que se encuentran en el contexto de definición de la plantilla o en el contexto de creación de instancias de la plantilla.
...
El siguiente código llamará a foo(void*)
y no a foo(S const &)
como podría esperarse.
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);
}
Es probable que esto no sea tan importante en sí mismo, pero sí resalta que para un compilador de C ++ totalmente compatible (es decir, uno con soporte para export
) la palabra clave static
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 hacerlo static
.
Actualización para C ++ moderno
A partir de C ++ ''11, los miembros de un espacio de nombres sin nombre tienen vínculos internos 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 del enlace (tomado de C ++ ''14):
Para una llamada a la función donde la expresión postfix 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 nombre no calificado (3.4.1), solo se encuentran las declaraciones de función 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 función encontradas en el contexto de definición de la plantilla o en el contexto de creación de instancias de la 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 los siguientes motivos:
es obvio y claro, solo desde la definición de la función, que es privado a la unidad de traducción donde se compila. Con el espacio de nombres sin nombre, es posible que deba desplazarse y buscar para ver si una función está en un espacio de nombres.
Las funciones en los espacios de nombres pueden ser tratadas como externas por algunos compiladores (más antiguos). En VS2017 siguen siendo externos. Por este motivo, incluso si una función está en un espacio de nombres sin nombre, es posible que desee marcarlos como estáticos.
las funciones estáticas se comportan de manera muy similar en C o C ++, mientras que los espacios de nombres sin nombre obviamente son solo C ++. los espacios de nombres sin nombre también agregan un nivel adicional si la sangría y no me gusta eso :)
Entonces, me alegra ver que el uso de estática para funciones ref .
Poner los métodos en un espacio de nombres anónimo le impide violar accidentalmente la Regla de una definición , lo que le permite nunca preocuparse por nombrar sus métodos auxiliares de la misma manera que a cualquier otro método que pueda vincular.
Y, como señala luke, los espacios de nombres anónimos son preferidos por el estándar sobre los miembros estáticos.
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 compiladores más antiguos a veces pueden tener problemas con esto. Actualmente trabajo con algunos compiladores para nuestras plataformas de destino, y el compilador más moderno de Linux está bien con la colocación de funciones en el espacio de nombres anónimo.
Pero un compilador más antiguo que se ejecuta en Solaris, con el que estamos casados hasta una versión futura no especificada, a veces lo aceptará y otras lo marcará como un error. El error no es lo que me preocupa, es lo que podría estar haciendo cuando lo acepta . Por lo tanto, hasta que seamos modernos en general, seguimos usando funciones estáticas (generalmente de clase) en las que preferiríamos el espacio de nombres anónimo.
Recientemente comencé a reemplazar palabras clave estáticas con espacios de nombres anónimos en mi código, pero inmediatamente tuve un problema donde las variables en el espacio de nombres ya no estaban disponibles para mi inspección en el 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 mis variables y funciones del módulo. De esa manera 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 es posible que desee utilizar en otro lugar.
Supongo que una desventaja potencial de este enfoque en comparación con uno verdaderamente anónimo es que alguien podría violar el alcance estático deseado utilizando el calificador de espacio de nombres en otros módulos. Aunque no sé si esa es una preocupación importante.
Se puede ver una diferencia específica del compilador entre los espacios de nombres anónimos y las funciones estáticas al compilar el siguiente código.
#include <iostream>
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;
}
Compilando este código con VS 2017 (especificando el indicador de advertencia de nivel 4 / W4 para habilitar el aviso C4505: se eliminó la función local sin referencia ) y gcc 4.9 con la función -Wunused-o el indicador de bloqueo muestra que VS 2017 solo producirá un aviso La función estática no utilizada. gcc 4.9 y versiones posteriores, así como el lenguaje lógico 3.3 y versiones posteriores, producirán 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.
El estándar de C ++ se 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á en desuso cuando se declaran objetos en un ámbito de espacio de nombres, el espacio de nombres sin nombre proporciona una alternativa superior.
La estática solo se aplica a los nombres de objetos, funciones y uniones anónimas, no a las declaraciones de tipo.
Editar:
La decisión de desaprobar este uso de la palabra clave estática (afecta la visibilidad de una declaración de variable en una unidad de traducción) se ha revertido ( ref ). En este caso, el uso de un espacio de nombres estático o sin nombre vuelve a ser esencialmente dos formas de hacer exactamente lo mismo. Para más discusión por favor vea this 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 this pregunta SO para más detalles.
El crédito es para Mike Percy por llamar mi atención sobre esto.