O que é poluição "usando espaço para nome"?

15

Eu estava olhando para o guia de codificação do Google [aqui] e eles não recomendam que se use o using namespaceou namespace::function- se eu não o interpretei mal.

Isso se aplica stdtambém? cout<<não funciona sem ele. Este livro recomenda o mesmo. Então, como faço para usar cout<<sem using namespace std;ou std::cout<<?

Qual é o caminho recomendado? std::cout<<? A maioria dos livros de texto em c ++ ensina aos iniciantes que using namespace std;eles estão propagando práticas ruins de codificação?

Lorde Loh.
fonte

Respostas:

18

Enquanto leio o padrão do Google, você não pode usar a using namespace foo;diretiva em nenhum lugar. Essa diretiva traz tudo declarado no espaço para nome e é uma causa comum de colisões e comportamento inesperado. Outros citaram um muito comum: você tem seu próprio método max ou min em algum lugar e ele colide em um arquivo src em que alguém inclui um cabeçalho no seu método e depois dizusing namespace std;

Em certos lugares, é permitido ter uma declaração de uso, que é da forma using ::foo::bar;

As pessoas gostam de usar diretivas em seu código, pois economiza muita digitação, mas isso traz riscos. Se você possui um arquivo com muitas instruções cout, entendo que não quero digitar std :: cout centenas de vezes, mas você pode simplesmente dizer usando :: std :: cout. Trato-os como declarações de variáveis: escopo-los onde são necessários. Se uma função em um arquivo de 10 precisar gravar a saída, não declare o caminho cout na parte superior, coloque-a nessa função que está realizando a saída real.

#include <ostream>
//using namespace std; // NO!
//using ::std::cout;   // less bad than using namespace, but I prefer to scope it

int main(int argc, char** argv)
{
   int rc = do_some_stuff(argc, argv);
   using ::std::endl;
   if (rc) { // print the success report
      using ::std::cout;
      cout << "The test run completed. The return code was " << rc << '.' << endl;
    } else {
      using ::std::cerr;
      cerr << "Unable to complete the test run." << endl;
    }
    return 0 == rc;
}

Isso é um pouco extremo, com apenas algumas linhas produzindo resultados, mas você entendeu.

Outra coisa que se pode fazer é alias ou typedef para minimizar a digitação. Não acho que std :: seja tão ruim assim, mas temos um enorme conjunto de fontes com várias dezenas de módulos e, às vezes, precisamos escrever códigos como esse console_gui::command_window::append("text"). Isso fica entediante depois de um tempo e causa muitas longas filas. Eu sou a favor de algo como

typedef console_gui::command_window cw;
cw::append("text");

desde que os aliases sejam feitos em um escopo local e mantenham contexto suficiente para tornar o código legível.

Michael Mathews
fonte
1
Obrigado! Isso é realmente útil. Você não apenas explicou por que é ruim com bons exemplos, mas também apontou soluções com bons exemplos. :-)
Lorde Loh.
1
BTW: Usar std::endlpara a liberação explícita em stdout/ stderrnormalmente é supérfluo, esses fluxos estão vinculados a stdout/ de stderrqualquer maneira. Até atrasa um pouco as coisas.
Deduplicator
8

Isso ocorre porque: 1) derrota todo o objetivo dos espaços para nome, que é reduzir a colisão de nomes; 2) disponibiliza ao espaço de nomes global o espaço de nome inteiro especificado com a diretiva using.

Por exemplo, se você incluir e definir sua própria função max (), ela colidirá com std :: max ().

http://en.cppreference.com/w/cpp/algorithm/max

A preferência é usar std :: member_you_wish_to_use porque indica explicitamente qual espaço para nome usar.

Torta de maçã
fonte
Presumo que isso significa que devo usar std::max()com o prefixo do espaço para nome. Ou estou enganado?
Senhor Loh.
3
Isso significa que se você colocar "using namespace std;" no seu código, você receberá erros se você definir a sua própria função max (ou qualquer outro nome já definido no namespace std)
Chewy Gumball
1
Significa apenas que você deve ter cuidado com as usingdiretivas, porque nesse caso, ela quebraria sua função max () se você tivesse definido uma e incluído <algoritmo>. Este é um caso simples, mas você nunca sabe o que pode quebrar. Você precisaria conhecer a biblioteca inteira para ter certeza de que não a quebrou, mas não pode saber se o seu código quebraria (por exemplo, colisão de nomes) no futuro.
precisa saber é o seguinte
6

Citando o link que você fornece:

Você pode usar uma declaração de uso em qualquer lugar de um arquivo .cc e em funções, métodos ou classes nos arquivos .h.

// OK em arquivos .cc.

// Deve estar em uma função, método ou classe nos arquivos .h.

using :: foo :: bar;

O estilo do Google proíbe o uso de espaços para nome no contexto global, mas permite fazê-lo nos locais.

Em todos os lugares em que o uso da declaração afeta apenas uma parte limitada e claramente visível do código, é perfeitamente aceitável.

Quando você polui o contexto global, o código não relacionado é afetado (por implicação, usando o cabeçalho). Nada acontece quando você faz isso no contexto local.

Basilevs
fonte
Nós temos os mesmos padrões. Algumas de nossas pessoas digitaram localmente um longo espaço para nome. por exemplo, typedef foolicious :: barlicious fb; fb :: bebida d;
Michael Mathews
1

eles não recomendam que se use o espaço para nome usando ornamespace: function` - se eu não o interpretou mal.

Você fez. A recomendação apenas se aplica à using namespacediretiva (que é comumente referida como abusing namespace, não totalmente humorística). É altamente preferido que você use o nome completo de uma função ou objeto, como std::cout.

H2CO3
fonte
1

Embora a pergunta já tenha respostas úteis, um detalhe parece ser muito curto.

A maioria dos programadores fica um pouco confusa com a usingpalavra - chave e as descrições de namespaceuso, mesmo que tentem aprender pesquisando a referência, porque declaração e diretiva são equivalentes, ambas são palavras longas relativamente abstratas, começando com d .

Os identificadores nos espaços para nome são acessíveis nomeando explicitamente o espaço para nome:

myNamespace::myIdent

pode haver muito mais teclas para digitar. Mas também pode diminuir a importância do seu código, se a maioria dos identificadores tiver o prefixo da mesma maneira. A usingpalavra-chave ajuda a evitar essas desvantagens do espaço para nome. Como usingfunciona no nível do compilador (não é macro), seu efeito dura todo o escopo em que é usado. É por isso que o estilo do Google restringe seu uso a escopos bem definidos, como classes nos arquivos de cabeçalho ou funções nos arquivos cpp.

... é claro que há uma diferença entre usar declaração

using myNamespace::myIdent; // make myIdent an alias of myNamespace::myIdent

e usando diretiva

using myNamespace; // make all identifiers of myNamespace directly accessible

Se usado em escopos enormes, o último leva a muito mais confusão.

Lobo
fonte
1

Aqui está:

#include <iostream>

int main()
{
    std::endl(std::operator<<(std::cout, "Hello world!"));
}

Ao escrevê-lo dessa maneira, evitamos ADL propensas a erros, além de usar diretivas e declarações.

Isso pretende ser uma resposta sarcástica. :-D

Estou com Herb Sutter no Google neste. Dos padrões de codificação C ++:

Você pode e deve usar o namespace usando declarações e diretivas liberalmente em seus arquivos de implementação após #include diretivas e se sentir bem com isso. Apesar de repetidas afirmações em contrário, o espaço para nome usando declarações e diretivas não é ruim e não anula o objetivo dos espaços para nome. Em vez disso, são eles que tornam os espaços para nome utilizáveis .

Você pode ficar obcecado com possíveis conflitos no espaço de nome que provavelmente nunca se manifestarão e provavelmente não serão difíceis de corrigir em um evento astronomicamente raro, evitando cuidadosamente as usingdiretivas e especificando explicitamente tudo o que você usa (até os operadores) com usingdeclarações ou apenas vá em frente e comece using namespace std. Eu recomendo este último do ponto de vista da produtividade.

A maioria dos livros de texto em c ++ ensina aos iniciantes o uso do namespace std; eles estão propagando más práticas de codificação?

O contrário, se você me perguntar, e acredito que Sutter acima concorda.

Agora, ao longo da minha carreira, encontrei cerca de três conflitos no espaço de nomes como resultado direto de using diretivas em bases de código que abrangem dezenas de milhões de LOC. No entanto, nos três casos, eles estavam em arquivos de origem que abrangiam mais de 50.000 linhas de código legado, originalmente escritas em C e depois bastardizadas para C ++, executando uma lista eclética maciça de funções díspares, incluindo cabeçalhos de uma dúzia de bibliotecas diferentes e tendo uma lista épica #includesque se estendeu por uma página. Apesar da bagunça épica, eles não eram muito difíceis de corrigir, pois causavam erros de compilação no OSX (o único SO em que o código não conseguiu compilar), não erros de execução. Não organize seu código dessa maneira assustadora e você ficará bem.

Dito isto, evitar tanto using directivas e declarações em arquivos de cabeçalho. Isso é simplesmente retardado. Mas para arquivos de origem, e especialmente aqueles que não têm uma página inteira cheia de #includediretivas, eu diria que não se preocupe se você não estiver trabalhando no Google.


fonte