Por que namespaces sem nome são usados ​​e quais são seus benefícios?

Acabei de me juntar a um novo projeto de software em C ++ e estou tentando entender o design. O projeto faz uso freqüente de namespaces sem nome. Por exemplo, algo como isto pode ocorrer em um arquivo de definição de class:

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

Quais são as considerações de design que podem causar o uso de um namespace sem nome? Quais são as vantagens e desvantagens?

(A seguir, as coisas com striked-through são coisas que não se aplicam mais ao C ++ 11, mas se aplicam ao C ++ 03. O C ++ 11 quase não faz mais diferenças (se houver, elas são apenas uma linguagem) diferenças de advogado que não me lembro)).

Namespaces sem nome são um utilitário para tornar um identificador efetivamente local de unidade de tradução. Eles se comportam como se você escolhesse um nome único por unidade de tradução para um namespace:

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

O passo extra usando o corpo vazio é importante, então você já pode se referir dentro do corpo do namespace para identificadores como ::name que são definidos naquele namespace, já que a diretiva using já ocorreu.

Isso significa que você pode ter funções gratuitas chamadas (por exemplo) de help que podem existir em várias unidades de tradução e não entrarão em conflito no momento do link, pois todas elas receberam um nome exclusivo devido ao namespace exclusivo em que estão . O efeito é quase idêntico ao uso da palavra-chave static usada em C, que você pode colocar na declaração de identificadores. static usada dessa maneira é preterida em C ++, uma vez que espaços de nome não nomeados são uma alternativa superior, podendo até tornar uma unidade de tradução de tipo local.

 namespace { int a1; } static int a2; 

Ambos são locais da unidade de tradução e não se chocam na hora do link. Mas a diferença é que o a1 no namespace anônimo obtém um nome exclusivo. Ele ainda possui binding externa e pode ser exportado para a tabela de símbolos do arquivo object que está sendo criado. Isso se torna importante se você quiser usar seu endereço como um argumento de modelo:

 template struct sample { }; // OK - a1 has external linkage sample< &a1> s1; // NOT OK - translation unit locality is done by giving a2 internal linkage. sample< &a2> s2; 

Os parâmetros do modelo devem ter binding externa, portanto, neste caso, o identificador deve ser colocado em um espaço de nomes anônimo.

Leia o excelente artigo na comeau-computing `Por que um namespace sem nome é usado em vez de estático? .

Ter algo em um namespace anônimo significa que ele é local para essa unidade de tradução (arquivo .cpp e todos os seus includes). Isso significa que se outro símbolo com o mesmo nome for definido em outro lugar, não haverá violação do ODR ( One Definition Rule ).

Isso é o mesmo que o modo C de ter uma variável global estática ou estática, mas ela também pode ser usada para definições de class (e deve ser usada em vez de static em C ++).

Todos os namespaces anônimos no mesmo arquivo são tratados como o mesmo namespace e todos os namespaces anônimos em arquivos diferentes são distintos. Um namespace anônimo é o equivalente a:

 namespace __unique_compiler_generated_identifer0x42 { ... } using namespace __unique_compiler_generated_identifer0x42; 

O exemplo mostra que as pessoas no projeto que você ingressou não entendem namespaces anônimos 🙂

 namespace { const int SIZE_OF_ARRAY_X; const int SIZE_OF_ARRAY_Y; 

Eles não precisam estar em um namespace anônimo, já que o object const já possui uma vinculação estática e, portanto, não pode entrar em conflito com identificadores do mesmo nome em outra unidade de tradução.

  bool getState(userType*,otherUserType*); } 

E isso é realmente uma getState() : getState() tem binding externa. Geralmente, é melhor preferir a binding estática, já que isso não polui a tabela de símbolos. É melhor escrever

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

Aqui. Eu caí na mesma armadilha (há palavras no padrão que sugerem que os arquivos-statics são de alguma forma obsoletos em favor de namespaces anônimos), mas trabalhando em um grande projeto C ++ como o KDE, você tem muitas pessoas que viram a cabeça do jeito certo volta novamente 🙂

Além das outras respostas a essa pergunta, o uso de um namespace anônimo também pode melhorar o desempenho. Como os símbolos dentro do namespace não precisam de nenhuma binding externa, o compilador é mais livre para realizar uma otimização agressiva do código dentro do namespace. Por exemplo, uma function que é chamada várias vezes uma vez em um loop pode ser embutida sem qualquer impacto no tamanho do código.

Por exemplo, no meu sistema, o código a seguir leva em torno de 70% do tempo de execução se o namespace anônimo for usado (x86-64 gcc-4.6.3 e -O2; observe que o código extra em add_val faz com que o compilador não queira include duas vezes).

 #include  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; } 

Um namespace anônimo torna as variables, funções, classs etc. incluídas apenas dentro desse arquivo. No seu exemplo, é uma maneira de evitar variables ​​globais. Não há tempo de execução ou diferença no desempenho de tempo de compilation.

Não há muita vantagem ou desvantagem além de “eu quero que esta variável, function, class, etc. seja pública ou privada?”

Namespace sem nome limita o access de class, variável, function e objects ao arquivo no qual ele está definido. A funcionalidade de namespace sem nome é semelhante à palavra-chave static em C / C ++.
static palavra-chave static limita o access da variável global e da function ao arquivo no qual elas estão definidas.
Há uma diferença entre o namespace sem nome e a palavra-chave static devido ao qual o namespace sem nome tem vantagem sobre estático. palavra-chave static pode ser usada com variável, function e objects, mas não com class definida pelo usuário.
Por exemplo:

 static int x; // Correct 

Mas,

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

Mas o mesmo pode ser possível com o namespace sem nome. Por exemplo,

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