Domanda Perché "usare namespace std" è considerato una cattiva pratica?


Mi è stato detto da altri che scrivere using namespace std nel codice è sbagliato, e che dovrei usare std::cout e std::cin direttamente invece.

Perché è using namespace std considerato una cattiva pratica? È inefficiente o rischia di dichiarare variabili ambigue (variabili che condividono lo stesso nome di una funzione in std namespace)? Ha un impatto sulle prestazioni?


2060
2017-09-21 03:08


origine


risposte:


Questo non è affatto correlato alle prestazioni. Ma considera questo: stai usando due librerie chiamate Foo e Bar:

using namespace foo;
using namespace bar;

Tutto funziona bene, puoi chiamare Blah() da Foo e Quux() da Bar senza problemi. Ma un giorno l'aggiornamento a una nuova versione di Foo 2.0, che ora offre una funzione chiamata Quux(). Ora hai un conflitto: Importa Foo 2.0 e Barra Quux() nel tuo spazio dei nomi globale. Ci vorrà un po 'di tempo per risolverlo, specialmente se i parametri della funzione coincidono.

Se tu avessi usato foo::Blah() e bar::Quux(), quindi l'introduzione di foo::Quux() sarebbe stato un non-evento.


1744
2017-09-21 03:13



Sono d'accordo con tutto Greg ha scritto, ma mi piacerebbe aggiungere: Può persino peggiorare di quanto ha detto Greg!

Library Foo 2.0 potrebbe introdurre una funzione, Quux(), è una corrispondenza senza ambiguità per alcune delle tue chiamate a Quux() rispetto al bar::Quux() il tuo codice ha richiesto anni. Quindi il tuo il codice è ancora compilato, ma chiama silenziosamente la funzione sbagliata e fa dio-sa-cosa. È quasi il male che le cose possono ottenere.

Tieni presente che il std namespace ha tonnellate di identificatori, molti dei quali sono molto quelli comuni (pensa list, sort, string, iterator, ecc.) che molto probabilmente appariranno anche in altri codici.

Se lo consideri improbabile: c'era una domanda posta qui su Stack Overflow dove praticamente esattamente questo è successo (funzione errata chiamata a causa di omessa std:: prefisso) circa sei mesi dopo aver dato questa risposta. Qui è un altro esempio più recente di una simile domanda. Quindi questo è un vero problema.


Ecco un altro punto dati: molti, molti anni fa, ho anche trovato fastidioso dover prefissare tutto dalla libreria standard con std::. Poi ho lavorato in un progetto in cui è stato deciso all'inizio che entrambi using le direttive e le dichiarazioni sono vietate ad eccezione degli ambiti delle funzioni. Indovina un po? La maggior parte di noi impiegava pochissime settimane per abituarsi a scrivere il prefisso, e dopo alcune settimane la maggior parte di noi ha persino accettato di aver effettivamente realizzato il codice più leggibile. C'è una ragione per questo: Se ti piace la prosa più corta o più lunga è soggettiva, ma i prefissi obiettivamente aggiungono chiarezza al codice. Non solo il compilatore, ma anche tu trovi più facile vedere a quale identificatore viene fatto riferimento.

In un decennio, quel progetto è cresciuto fino ad avere diverse milioni di linee di codice. Dal momento che queste discussioni si ripetono continuamente, una volta ero curioso di sapere con quale frequenza (consentita) la portata delle funzioni usingeffettivamente è stato utilizzato nel progetto. Ho capito le fonti e ho trovato solo una o due decine di posti dove è stato usato. Per me questo indica che, una volta provato, gli sviluppatori non lo trovano std:: abbastanza doloroso da impiegare le direttive anche una volta ogni 100 kLoC, anche dove è stato permesso l'uso.


Bottom line: il prefisso esplicitamente di tutto non fa male, richiede poco tempo per abituarsi e ha vantaggi oggettivi. In particolare, rende il codice più facile da interpretare dal compilatore e dai lettori umani - e questo dovrebbe essere probabilmente l'obiettivo principale quando si scrive codice.


1146
2017-09-21 09:26



Penso che sia brutto metterlo nei file di intestazione delle tue classi: perché in quel caso dovresti forzare chiunque voglia usare le tue classi (includendo i tuoi file di intestazione) anche a "usare" (cioè vedere tutto) quegli altri spazi dei nomi .

Tuttavia, potresti sentirti libero di inserire un'istruzione using nei tuoi file * privati ​​.cpp.


Fai attenzione che alcune persone non sono d'accordo con il mio modo di dire "sentiti libero" in questo modo - perché anche se è un'istruzione using in un file cpp meglio che in un'intestazione (perché non influisce sulle persone che includono il file di intestazione), pensano che non lo sia ancora bene (perché a seconda del codice potrebbe rendere più difficile mantenere l'implementazione della classe). Questo argomento FAQ dice,

La direttiva using esiste per il codice C ++ legacy e per facilitare la transizione ai namespace, ma probabilmente non dovresti usarlo regolarmente, almeno non nel tuo nuovo codice C ++.

Suggerisce due alternative:

  • Una dichiarazione usando:

    using std::cout; // a using-declaration lets you use cout without qualification
    cout << "Values:";
    
  • Passaci sopra e digita semplicemente std ::

    std::cout << "Values:";
    

308
2017-09-21 03:22



Recentemente mi sono imbattuto in un reclamo Visual Studio 2010. Si è scoperto che praticamente tutti i file sorgente avevano queste due linee:

using namespace std;
using namespace boost;

Molti Incremento le caratteristiche stanno entrando nello standard C ++ 0x e Visual Studio 2010 ha molte caratteristiche di C ++ 0x, quindi improvvisamente questi programmi non stavano compilando.

Pertanto, evitando using namespace X; è una forma di prova del futuro, un modo per assicurarsi che una modifica alle librerie e / o ai file di intestazione in uso non interrompa un programma.


197
2017-10-28 17:37



Versione breve: non utilizzare il globale usando dichiarazioni o direttive nei file di intestazione. Sentiti libero di usarli nei file di implementazione. Ecco cosa Herb Sutter e Andrei Alexandrescu hanno da dire su questo problema in Standard di codifica C ++ (audace per l'enfasi è mia):

Sommario

Gli usi dello spazio dei nomi sono per tua comodità, non per te da infliggere agli altri: non scrivere mai una dichiarazione usando o una direttiva using prima di una direttiva #include.

Corollario: nei file di intestazione, non scrivere a livello di spazio dei nomi utilizzando le direttive o utilizzando le dichiarazioni; invece, esplicitamente namespace qualifica tutti i nomi. (La seconda regola segue dal primo, perché le intestazioni non possono mai sapere quale altra intestazione #include potrebbe apparire dopo di esse.)

Discussione

In breve: puoi e dovresti usare namespace usando dichiarazioni e direttive liberamente nei tuoi file di implementazione dopo le direttive #include e sentirti bene a riguardo. Nonostante ripetute asserzioni contrarie, lo spazio dei nomi che usa le dichiarazioni e le direttive non è malvagio e non sconfigge lo scopo dei namespace. Piuttosto, sono ciò che rende utilizzabili gli spazi dei nomi.


159
2017-11-03 20:00



Non si dovrebbe usare l'uso della direttiva a livello globale, specialmente nelle intestazioni. Tuttavia ci sono situazioni in cui è appropriato anche in un file di intestazione:

template <typename FloatType> inline
FloatType compute_something(FloatType x)
{
    using namespace std; //no problem since scope is limited
    return exp(x) * (sin(x) - cos(x * 2) + sin(x * 3) - cos(x * 4));
}

Questo è meglio della qualifica esplicita (std::sin, std::cos...) perché è più breve e ha la capacità di lavorare con i tipi a virgola mobile definiti dall'utente (tramite Argument Dependent Lookup).


103
2017-09-21 15:47



Non usarlo globalmente

È considerato "cattivo" solo quando usato globalmente. Perché:

  • Si confonde lo spazio dei nomi in cui si sta programmando.
  • I lettori avranno difficoltà a vedere da dove proviene un identificatore particolare, quando ne usi molti using namespace xyz.
  • Qualunque cosa sia vera per altro i lettori del tuo codice sorgente sono ancora più veri per il lettore più frequente di esso: te stesso. Torna tra un anno o due e dai un'occhiata ...
  • Se parli solo di using namespace std potresti non essere consapevole di tutte le cose che tieni - e quando ne aggiungi un'altra #include o passare a una nuova revisione di C ++ potresti ottenere conflitti di nomi di cui non eri a conoscenza.

Puoi usarlo localmente

Vai avanti e usalo a livello locale (quasi) liberamente. Questo, ovviamente, ti impedisce di ripetere std:: - e anche la ripetizione è cattiva.

Un idioma per usarlo localmente

In C ++ 03 c'era un idioma - codice boilerplate - per implementare a swap funzione per le tue lezioni. È stato suggerito di utilizzare effettivamente un locale using namespace std -- o quantomeno using std::swap:

class Thing {
    int    value_;
    Child  child_;
public:
    // ...
    friend void swap(Thing &a, Thing &b);
};
void swap(Thing &a, Thing &b) {
    using namespace std;      // make `std::swap` available
    // swap all members
    swap(a.value_, b.value_); // `std::stwap(int, int)`
    swap(a.child_, b.child_); // `swap(Child&,Child&)` or `std::swap(...)`
}

Questo fa la seguente magia:

  • Il compilatore sceglierà il std::swap per value_, cioè void std::swap(int, int).
  • Se hai un sovraccarico void swap(Child&, Child&) implementato il compilatore lo sceglierà.
  • Se fate non ha quel sovraccarico che userà il compilatore void std::swap(Child&,Child&) e fai del suo meglio per scambiarli.

Con C ++ 11 non c'è più ragione per usare questo modello. L'implementazione di std::swap è stato cambiato per trovare un potenziale sovraccarico e sceglierlo.


80
2018-01-18 09:34