Use `using` em C ++ ou evite-o?

17

Descontando semânticas sutilmente diferentes devido a ADL, como devo usar geralmente usinge por quê? É dependente da situação (por exemplo, cabeçalho que será #included vs. arquivo de origem que não será)?

Além disso, devo preferir ::std::ou std::?

  1. No nível do espaço para nome using namespace:

    using namespace std;
    
    pair<string::const_iterator, string::const_iterator>
    f(const string &s) {
        return make_pair(s.begin(), s.end());
    }
    
  2. Sendo totalmente explícito:

    std::pair<std::string::const_iterator, std::string::const_iterator>
    f(const std::string &s) {
        return std::make_pair(s.begin(), s.end());
    }
    
  3. Usando declarações de nível de espaço para nome:

    using std::pair;
    using std::string;
    
    pair<string::const_iterator, string::const_iterator>
    f(const string &s) {
        return make_pair(s.begin(), s.end());
    }
    
  4. Função-local usando declarações:

    std::pair<std::string::const_iterator, std::string::const_iterator>
    f(const std::string &s) {
        using std::make_pair;
        return make_pair(s.begin(), s.end());
    }
  5. Função local using namespace:

    std::pair<std::string::const_iterator, std::string::const_iterator>
    f(const std::string &s) {
        using namespace std;
        return make_pair(s.begin(), s.end());
    }
  6. Algo mais?

Isso pressupõe pré-C ++ 14 e, portanto, nenhuma dedução de tipo de retorno usando auto.

Mehrdad
fonte
2
Consulte stackoverflow.com/questions/1265039/using-std-namespace para obter um ponto de partida.
APROGRAMMER #
@ AProgrammer: Ah, obrigado pelo link, que responde parte da minha pergunta. :) Ainda querendo saber sobre ::std::vs. std::embora.
Mehrdad
4
Estou usando stdsem segundo embora. Alguém que define um espaço para nome std está solicitando problemas (e provavelmente buscando tirar vantagem que a maioria das pessoas está usando stde não ::std).
APROGRAMMER #

Respostas:

25

Evite usar usingnos cabeçalhos, pois isso quebra o objetivo dos espaços para nome.

Não há problema em usá-lo nos arquivos de origem, mas eu ainda o evitaria em alguns casos (por exemplo using std).

No entanto, se você tiver namespaces aninhados, tudo bem:

namespace A {
namespace B {
namespace C {
class s;
} // C
} // B
namespace D{
using B::C::s;
} // D
} // A
BЈовић
fonte
6
Com +1, é incrível quantos tutoriais e cursos universitários apenas pedem para você usar a usingpalavra - chave sem uma explicação completa do motivo pelo qual os namespaces são usados.
Jeffrey Sweeney
As pessoas querem continuar usando iostreams, strings e assim por diante. Eles não querem digitar std :: toda vez que quiserem usar alguma coisa ou precisam se lembrar de outro pedaço de clichê para colocar antes do código, o que causará erros menos que úteis se esquecerem . :(
Colen
Seria algo como typedef std :: string sstring; ser uma alternativa?
Giorgio
1
@ Colen: Essas pobres almas podem usar using std::coute amigos, mas não couté como se já fosse um nome horrivelmente longo.
Benjamin Bannier
1
Se você é um estudante universitário no seu primeiro dia de "minha primeira aula de C ++", é outra coisa que pode causar erros de sintaxe que você não entende. É fácil descobrirmos porque somos programadores experientes, mas quando você está tentando aprender o idioma, é mais uma coisa para se preocupar com o que você não precisa.
Colen
11

Ao colocar uma instrução using em um arquivo de origem, POR FAVOR, basta inserir o que você precisa. Por exemplo:

using std::string;
using std::ostringstream;

A questão aqui é que, se você fizer

using namespace std;

você puxa TODA ÚNICA COISA de std no espaço para nome global. O que leva a mensagens de erro muito interessantes quando você acidentalmente usa um nome em seu código que corresponde a um que você desconhecia completamente no std. Se você apenas puxar o que deseja, não terá esse problema (ou, mais precisamente, o próximo programador a trabalhar no seu código não terá esse problema).

Michael Kohne
fonte
Como alternativa, você pode using namespaceapenas no escopo de uma função, evitando o problema.
Tamás Szelei
2
@fish - na verdade, fazer 'using namespace' no escopo da função não evita o problema, apenas limita o espaço onde as coisas podem dar errado. E se você acabar colocando 'using namespace' em todas as funções, não será muito diferente de fazê-lo globalmente.
Michael Kohne
Embora o C ++ realmente permita declarar tipos no nível da função, isso não é algo comum; além disso, é fácil identificar os possíveis conflitos de nome na saída do compilador (mas você está certo de que isso não os impede).
Tamás Szelei
2

Como VJovic indica, não use usingem um arquivo de cabeçalho. usingem um arquivo de cabeçalho afeta a unidade de compilação atual (o arquivo .cpp) de maneiras que o arquivo de origem pode não estar esperando.

using namespacetambém deve ser evitado em um arquivo de origem. Isso coloca todos os símbolos no mesmo escopo que o arquivo de origem. É mais claro para seus leitores o que você está fazendo se usar símbolos específicos no espaço para nome.

Bill Door
fonte
2
Como uma preocupação prática, a menos que seu código substitua nomes comumente usados, prefiro ver using namespace JoystickModuleno início de um arquivo .cpp do que JoystickModule::anexá-lo a todos os objetos.
Alex P
@ AlexP: É exatamente assim que eu faço. No usingmomento, estou trabalhando em uma declaração para meu próprio espaço de nome e todo o resto permanece no espaço de nome.
Benjamin Kloster
Devo elaborar sobre "usar símbolos específicos do espaço para nome". Em vez de prefixar cada símbolo em cada uso, o que não ajuda na legibilidade, prefiro usar a elevação explícita de símbolos. using SomeNameSpace::SomeSymbol. Isso evita mover todos os símbolos do espaço para nome para o escopo atual.
Bill Porta
0

Escrever usingem cabeçalhos é a melhor maneira de criar todos os tipos de erros desagradáveis e impossíveis de depurar . Você não fazer isso.

Escrever using namespace XYZno arquivo de origem é um pouco melhor, mas ainda pode causar inúmeras dores de cabeça. A maneira segura é especificar explicitamente o que você está usando, por exemplo using Foo::Bar.

Digamos que você tenha o Bar.cpp com o seguinte:

//Bar.cpp
using namespace Foo;
namespace
{
    double increment(double v) { return (v + 1); }
}

void Bar::someFunction()
{
    //...
    int currentIndex = 0;
    int nextIndex = increment(currentIndex);
    //...
}

A função funcionou bem, até que um dia - aparentemente sem nenhuma alteração de código nas classes relevantes - seu comportamento mudou: de repente, currentIndexsempre parece estar desligado um . Ao pesquisar nas alterações recentes, você descobre que não há alterações nem remotamente relacionadas ao código.

Eventualmente, você descobre a causa:
você (indiretamente) inclui em Foo.halgum lugar. Nos arquivos do namespace Foo, uma nova função foi adicionada:

//Foo.h
namespace Foo
{
    //...
    int& increment(int& v) { v += 1; return v; };
    //...
}

Qual é uma correspondência inequivocamente melhor do increment(int)que a sua função increment(double)- então agora a Foo::increment()função é chamada por Bar::someFunction(). Ops.

(E se você escrever usingnos cabeçalhos, using namespace Foopode muito bem estar em qualquer lugar da sua árvore de inclusão ...)

Então ... Não escreva nenhum usingem Cabeçalhos e também tenha cuidado ao escrever using namespaceem arquivos de origem.

CharonX
fonte