¿Por qué se utilizan espacios de nombres sin nombre y cuáles son sus beneficios?

242

Me acabo de unir a un nuevo proyecto de software C ++ y estoy tratando de entender el diseño. El proyecto hace uso frecuente de espacios de nombres sin nombre. Por ejemplo, algo así puede ocurrir en un archivo de definición de clase:

// newusertype.cc
namespace {
  const int SIZE_OF_ARRAY_X;
  const int SIZE_OF_ARRAY_Y;
  bool getState(userType*,otherUserType*);
}

newusertype::newusertype(...) {...

¿Cuáles son las consideraciones de diseño que pueden hacer que uno use un espacio de nombres sin nombre? ¿Cuáles son las ventajas y desventajas?

Scottie T
fuente

Respuestas:

189

Los espacios de nombres sin nombre son una utilidad para hacer que una unidad de traducción del identificador sea local. Se comportan como si eligiera un nombre único por unidad de traducción para un espacio de nombres:

namespace unique { /* empty */ }
using namespace unique;
namespace unique { /* namespace body. stuff in here */ }

El paso adicional que usa el cuerpo vacío es importante, por lo que ya puede referirse dentro del cuerpo del espacio de nombres a identificadores como ::nameese que están definidos en ese espacio de nombres, ya que la directiva de uso ya tuvo lugar.

Esto significa que puede tener funciones gratuitas llamadas (por ejemplo) helpque pueden existir en múltiples unidades de traducción, y no chocarán en el momento del enlace. El efecto es casi idéntico al uso de la staticpalabra clave utilizada en C que puede incluir en la declaración de identificadores. Los espacios de nombres sin nombre son una alternativa superior, pudiendo incluso hacer que una unidad de traducción de tipo sea local.

namespace { int a1; }
static int a2;

Ambas ason unidades de traducción locales y no chocarán en el momento del enlace. Pero la diferencia es que el a1espacio de nombres anónimo obtiene un nombre único.

Lea el excelente artículo en comeau-computing ¿Por qué se utiliza un espacio de nombres sin nombre en lugar de estático? ( Archive.org espejo ).

Johannes Schaub - litb
fuente
Explicas la relación con static. ¿Puedes por favor también comparar con __attribute__ ((visibility ("hidden")))?
phinz
74

Tener algo en un espacio de nombres anónimo significa que es local para esta unidad de traducción (archivo .cpp y todo lo que incluye), esto significa que si se define otro símbolo con el mismo nombre en otra parte, no habrá una violación de la Regla de definición única (ODR).

Esto es lo mismo que la forma en C de tener una variable global estática o una función estática, pero también puede usarse para definiciones de clase (y debería usarse en lugar de staticen C ++).

Todos los espacios de nombres anónimos en el mismo archivo se tratan como el mismo espacio de nombres y todos los espacios de nombres anónimos en diferentes archivos son distintos. Un espacio de nombres anónimo es el equivalente de:

namespace __unique_compiler_generated_identifer0x42 {
    ...
}
using namespace __unique_compiler_generated_identifer0x42;
Motti
fuente
14

El espacio de nombres sin nombre limita el acceso de clase, variable, función y objetos al archivo en el que se define. La funcionalidad del espacio de nombres sin nombre es similar a la staticpalabra clave en C / C ++.
staticLa palabra clave limita el acceso de la variable global y la función al archivo en el que se definen.
Hay una diferencia entre el espacio de nombres sin nombre y la staticpalabra clave debido a que el espacio de nombres sin nombre tiene ventaja sobre la estática. staticLa palabra clave se puede usar con variables, funciones y objetos, pero no con la clase definida por el usuario.
Por ejemplo:

static int x;  // Correct 

Pero,

static class xyz {/*Body of class*/} //Wrong
static structure {/*Body of structure*/} //Wrong

Pero lo mismo puede ser posible con un espacio de nombres sin nombre. Por ejemplo,

 namespace {
           class xyz {/*Body of class*/}
           static structure {/*Body of structure*/}
  } //Correct
Sachin
fuente
13

Además de las otras respuestas a esta pregunta, el uso de un espacio de nombres anónimo también puede mejorar el rendimiento. Como los símbolos dentro del espacio de nombres no necesitan ningún enlace externo, el compilador es más libre para realizar una optimización agresiva del código dentro del espacio de nombres. Por ejemplo, una función que se llama varias veces una vez en un bucle puede integrarse sin ningún impacto en el tamaño del código.

Por ejemplo, en mi sistema, el siguiente código toma alrededor del 70% del tiempo de ejecución si se utiliza el espacio de nombres anónimo (x86-64 gcc-4.6.3 y -O2; tenga en cuenta que el código adicional en add_val hace que el compilador no quiera incluir dos veces)

#include <iostream>

namespace {
  double a;
  void b(double x)
  {
    a -= x;
  }
  void add_val(double x)
  {
    a += x;
    if(x==0.01) b(0);
    if(x==0.02) b(0.6);
    if(x==0.03) b(-0.1);
    if(x==0.04) b(0.4);
  }
}

int main()
{
  a = 0;
  for(int i=0; i<1000000000; ++i)
    {
      add_val(i*1e-10);
    }
  std::cout << a << '\n';
  return 0;
}
xioxox
fuente
55
Demasiado bueno para ser verdad: probé este segmento en gcc 4-1-2, usando la optimización de O3, con y sin la declaración del espacio de nombres: -> Obtuve el mismo tiempo (3 segundos, con -O3 y 4 segundos con -O3)
Theo
2
Este código fue intencionalmente complejo para tratar de persuadir al compilador de que no esté en línea by add_val en main. La optimización de O3 utiliza muchas líneas, independientemente del costo de codificar la hinchazón. Sin embargo, todavía hay funciones probables donde O3 no estaría en línea add_val. Podría intentar hacer que add_val sea más complejo o llamarlo varias veces desde main en diferentes circunstancias.
xioxox
55
@Daniel: ¿qué me estoy perdiendo? según lo leído, usted dijo que se compara -O3con sí mismo, luego dijo que 3 vs 4 segundos son "al mismo tiempo". ninguno de estos tiene un poco de sentido. Sospecho que la explicación real sería, pero ¿qué es?
underscore_d
@underscore_d La respuesta indica -O2 se usó en ambos casos, no -O3. Los diferentes niveles de optimización pueden comportarse de manera diferente. Además, las diferentes versiones del compilador pueden comportarse de manera diferente (la respuesta puede estar desactualizada, es decir)
Paul Stelian
1
@PaulStelian Lo sé, pero parece bastante claro que estaba respondiendo no a la respuesta de xioxox sino al comentario de Theo (aunque su nombre ha cambiado o me confundí de alguna manera)
subrayado_d
12

El ejemplo muestra que las personas en el proyecto al que se unió no entienden espacios de nombres anónimos :)

namespace {
    const int SIZE_OF_ARRAY_X;
    const int SIZE_OF_ARRAY_Y;

No es necesario que estén en un espacio de nombres anónimo, ya que el constobjeto ya tiene un enlace estático y, por lo tanto, no puede entrar en conflicto con identificadores del mismo nombre en otra unidad de traducción.

    bool getState(userType*,otherUserType*);
}

Y esto es en realidad una pesimización: getState()tiene un vínculo externo. Por lo general, es mejor preferir el enlace estático, ya que eso no contamina la tabla de símbolos. Es mejor escribir

static bool getState(/*...*/);

aquí. Caí en la misma trampa (hay una redacción en el estándar que sugiere que los archivos estáticos de alguna manera están en desuso a favor de espacios de nombres anónimos), pero al trabajar en un gran proyecto de C ++ como KDE, obtienes muchas personas que vuelven la cabeza de la manera correcta alrededor de nuevo :)

Marc Mutz - mmutz
fuente
10
Dado que los espacios de nombres sin nombre de c ++ 11 tienen enlaces internos (sección 3.5 en el estándar o en.cppreference.com/w/cpp/language/namespace#Unnamed_namespaces )
Emile Vrijdags
11
"Estos no necesitan estar en un espacio de nombres en el anonimato" Técnicamente, seguro - pero aún así, no se pierde nada para ponerlos en una, como un recordatorio visual de su semántica y para que sea (incluso más) trivial para eliminar constNess más tarde si lo desea. ¡Dudo que eso signifique que el equipo de OP "no entiende" nada! Además, el bit sobre las funciones en espacios de nombres anónimos que tienen un enlace externo es incorrecto en C ++ 11 en adelante, como se señaló. Según tengo entendido, solucionaron un problema de argumentos de plantillas que anteriormente necesitaban enlaces externos, por lo que podrían permitir que los espacios de nombres sin nombre (capaces de contener argumentos de plantillas) tengan enlaces internos.
underscore_d
11

Un espacio de nombres anónimo hace que las variables, funciones, clases, etc. incluidas solo estén disponibles dentro de ese archivo. En su ejemplo, es una forma de evitar variables globales. No hay diferencia de rendimiento en tiempo de ejecución o tiempo de compilación.

No hay tanta ventaja o desventaja aparte de "¿quiero que esta variable, función, clase, etc. sea pública o privada?"

Max Lybbert
fuente
2
Puede haber diferencias de rendimiento: vea mi respuesta aquí. Permite al compilador optimizar el código mejor.
xioxox
2
Usted tiene un punto; al menos tan lejos como C ++ hoy en día. Sin embargo, C ++ 98 / C ++ 03 requiere que las cosas tengan un enlace externo para poder usarse como argumentos de plantilla. Dado que las cosas en espacios de nombres anónimos están disponibles como argumentos de plantilla, tendrían un enlace externo (al menos en pre-C ++ 11) incluso si no hubiera forma de referirse a ellos desde fuera del archivo. Creo que puede haber habido cierta capacidad para eludir eso, porque el estándar solo requiere que las cosas actúen como si las reglas se hicieran cumplir; y a veces es posible hacer eso sin hacer cumplir realmente las reglas.
Max Lybbert