Por que os namespaces não nomeados são usados ​​e quais são seus benefícios?


242

Acabei de ingressar em um novo projeto de software C ++ e estou tentando entender o design. O projeto faz uso frequente de namespaces não nomeados. Por exemplo, algo como isto pode ocorrer em um arquivo de definição de classe:

// 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 levar alguém a usar um espaço para nome sem nome? Quais são as vantagens e desvantagens?

Respostas:


189

Os espaços para nome sem nome são um utilitário para tornar local uma unidade de conversão de identificador. Eles se comportam como se você escolhesse um nome exclusivo por unidade de tradução para um espaço para nome:

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

A etapa extra usando o corpo vazio é importante, portanto, você já pode consultar no corpo do espaço para nome identificadores como ::nameos definidos nesse espaço para nome, pois a diretiva using já ocorreu.

Isso significa que você pode ter funções gratuitas chamadas (por exemplo) helpque podem existir em várias unidades de tradução e elas não entrarão em conflito no momento do link. O efeito é quase idêntico ao uso da staticpalavra - chave usada em C, que você pode inserir na declaração de identificadores. Os namespaces sem nome são uma alternativa superior, podendo até tornar uma unidade de tradução de tipo local.

namespace { int a1; }
static int a2;

Ambos asão unidades de tradução locais e não entrarão em conflito no momento do link. Mas a diferença é que o a1espaço para nome anônimo recebe um nome exclusivo.

Leia o excelente artigo em comeau-computing Por que um espaço para nome sem nome é usado em vez de estático? ( Espelho do Archive.org ).


Você explica a relação com static. Você também pode comparar com __attribute__ ((visibility ("hidden")))?
phinz 22/03

74

Ter algo em um espaço de nome anônimo significa que é local para esta unidade de tradução (arquivo .cpp e todas as suas inclusões) significa que se outro símbolo com o mesmo nome for definido em outro lugar, não haverá violação da regra de definição única (ODR).

É o mesmo que o modo C de ter uma variável global estática ou função estática, mas também pode ser usado para definições de classe (e deve ser usado em vez de staticem C ++).

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

namespace __unique_compiler_generated_identifer0x42 {
    ...
}
using namespace __unique_compiler_generated_identifer0x42;

14

O espaço para nome sem nome limita o acesso de classe, variável, função e objetos ao arquivo no qual está definido. A funcionalidade de namespace sem nome é semelhante à staticpalavra - chave em C / C ++.
staticA palavra-chave limita o acesso da variável global e da função ao arquivo no qual elas estão definidas.
Há diferença entre o espaço para nome e a staticpalavra - chave sem nome, pois o espaço para nome sem nome tem vantagem sobre a estática. staticA palavra-chave pode ser usada com variáveis, funções e objetos, mas não com a classe 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 espaço para nome sem nome. Por exemplo,

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

13

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

Por exemplo, no meu sistema, o código a seguir ocupa cerca de 70% do tempo de execução se o espaço para nome 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 incluir duas vezes).

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

5
Bom demais para ser verdade - tentei esse segmento no gcc 4-1-2, usando a otimização O3, com e sem a instrução namespace: -> Obtive o mesmo tempo (3seg, com -O3 e 4seg com -O3)
Theo

2
Esse código era intencionalmente complexo para tentar convencer o compilador a não incorporar be add_val no main. A otimização da O3 usa muita inlining, independentemente do custo de inchar o código. No entanto, ainda existem funções prováveis ​​em que o O3 não incorporaria o add_val. Você pode tentar tornar add_val mais complexo ou chamá-lo várias vezes do main em diferentes circunstâncias.
Xxox

5
@ Daniel: o que estou perdendo? conforme lido, você disse que se comparava -O3a si próprio e disse que 3 vs 4 segundos são "ao mesmo tempo". nenhum desses faz um pouco de sentido. Eu suspeito que a explicação real seria, mas o que é isso?
Underscore_d

@underscore_d A resposta indica -O2 foi usada nos dois casos, não -O3. Diferentes níveis de otimização podem se comportar de maneira diferente. Além disso, diferentes versões do compilador podem se comportar de forma diferente (a resposta pode ficar desatualizado, que é)
Paul Stelian

1
@PaulStelian Eu sei disso, mas parece bastante claro que eu estava respondendo a não resposta de xioxox mas sim ao comentário de Theo (embora seja o seu nome mudou ou eu se confundiu alguma forma)
underscore_d

12

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

namespace {
    const int SIZE_OF_ARRAY_X;
    const int SIZE_OF_ARRAY_Y;

Eles não precisam estar em um espaço para nome anônimo, pois o constobjeto já possui ligação estática e, portanto, não pode entrar em conflito com identificadores com o mesmo nome em outra unidade de tradução.

    bool getState(userType*,otherUserType*);
}

E isso é realmente uma pessimização: getState()tem ligação externa. Geralmente é melhor preferir ligação estática, pois 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 a estática dos arquivos é de alguma forma preterida em favor de namespaces anônimos), mas trabalhando em um grande projeto C ++ como o KDE, você recebe muitas pessoas que viram sua cabeça do jeito certo por aí novamente :)


10
Uma vez que c ++ 11 espaços de nomes sem nome tem ligação interna (secção 3.5 no padrão ou en.cppreference.com/w/cpp/language/namespace#Unnamed_namespaces )
Emile Vrijdags

11
"Eles não precisam estar em um espaço de nome anônimo" Tecnicamente, com certeza - mas ainda assim, não é necessário colocá-los em um, como um lembrete visual de sua semântica e torná-lo (ainda mais) trivial para remover o constness mais tarde, se desejar. Duvido que isso signifique que a equipe do OP "não entenda" nada! Além disso, o pouco sobre funções em espaços para nome anônimos com ligação externa está errado no C ++ 11 em diante, conforme observado. Pelo meu entendimento, eles corrigiram um problema de argumentos de modelo que anteriormente necessitavam de ligação externa, para permitir que espaços de nome sem nome (capazes de conter argumentos de modelo) tenham ligação interna.
underscore_d

11

Um espaço de nome anônimo torna as variáveis, funções, classes etc. disponíveis apenas dentro desse arquivo. No seu exemplo, é uma maneira de evitar variáveis ​​globais. Não há diferença de desempenho em tempo de execução ou tempo de compilação.

Não há tanta vantagem ou desvantagem além de "quero que essa variável, função, classe etc. seja pública ou privada?"


2
Pode haver diferenças de desempenho - veja minha resposta aqui. Ele permite que o compilador otimize melhor o código.
Xxox 29/08/14

2
Você tem um ponto; pelo menos tanto quanto C ++ hoje é. No entanto, as coisas necessárias para o C ++ 98 / C ++ 03 têm ligação externa para serem usadas como argumentos de modelo. Como as coisas nos namespaces anônimos estão disponíveis como argumentos de modelo, elas teriam ligação externa (pelo menos no período anterior ao C ++ 11), mesmo que não houvesse como referenciá-las de fora do arquivo. Eu acho que pode ter havido alguma capacidade de falsificar isso, porque o padrão exige apenas que as coisas ajam como se as regras fossem aplicadas; e às vezes é possível fazer isso sem realmente aplicar as regras.
precisa saber é o seguinte
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.