Che cos'è l'inquinamento da "utilizzo dello spazio dei nomi"?


15

Stavo guardando la guida alla codifica di Google [qui] e loro non mi consigliano di usare la using namespaceo namespace::function- se non l'ho interpretata male.

Questo vale anche per std? cout<<non funziona senza di essa. Questo libro raccomanda lo stesso. Quindi, come posso fare per usare cout<<senza using namespace std;o std::cout<<?

Qual è il modo raccomandato? std::cout<<? La maggior parte dei libri di testo in c ++ insegna ai principianti con la using namespace std;loro propagazione di cattive pratiche di programmazione?

Risposte:


18

Mentre leggo lo standard di Google, non puoi usare la using namespace foo;direttiva da nessuna parte. Questa direttiva porta tutto quanto dichiarato nello spazio dei nomi ed è una causa comune di collisioni e comportamenti imprevisti. Altri ne hanno citato uno molto comune: hai il tuo metodo max o min da qualche parte e si scontra in un file src in cui qualcuno include un'intestazione con il tuo metodo e poi diceusing namespace std;

In alcuni luoghi, è consentito avere una dichiarazione di utilizzo, che è del modulo using ::foo::bar;

Alla gente piace inserire le direttive nel proprio codice perché consente di risparmiare molta digitazione, ma comporta dei rischi. Se hai un file con molte istruzioni cout, posso capire di non voler scrivere std :: cout cento volte, ma puoi semplicemente dire usando :: std :: cout. Le tratto come dichiarazioni di variabili: le scope dove sono necessarie. Se una funzione in un file di 10 deve scrivere l'output, non dichiarare il modo cout in alto, inserirlo in quella funzione che sta eseguendo l'output effettivo.

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

È un po 'estremo con solo un paio di righe che producono output, ma hai l'idea.

Un'altra cosa che si può fare è alias o typedef per ridurre al minimo la digitazione. Non trovo std :: qualunque cosa sia così male, ma abbiamo un enorme set di sorgenti con diverse decine di moduli e talvolta dobbiamo scrivere codice come console_gui::command_window::append("text"). Questo diventa noioso dopo un po 'e provoca molte lunghe file. Sono tutto per qualcosa di simile

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

purché gli alias siano eseguiti in un ambito locale e mantengano un contesto sufficiente per rendere leggibile il codice.


1
Grazie! Questo è davvero utile. Non solo hai spiegato perché è male con esempi interessanti, ma hai anche sottolineato soluzioni con esempi interessanti. :-)
Lord Loh.

1
A proposito: usando std::endlper il flush esplicito su stdout/ stderrè normalmente abbastanza superfluo, quei flussi sono collegati a stdout/ stderrcomunque. Rallenta anche un po 'le cose.
Deduplicatore,

8

Questo perché: 1) sconfigge l'intero scopo degli spazi dei nomi, che è quello di ridurre la collisione dei nomi; 2) rende disponibile allo spazio dei nomi globale l'intero spazio dei nomi specificato con la direttiva using.

Ad esempio, se includi e definisci la tua funzione max (), si scontrerà con std :: max ().

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

La preferenza è usare std :: member_you_wish_to_use perché indica esplicitamente quale spazio dei nomi usare.


Presumo che questo significhi che dovrei usare std::max()con il prefisso dello spazio dei nomi. O mi sbaglio?
Lord Loh.

3
Vuol dire che se metti "using namespace std;" nel tuo codice, otterrai errori se definisci la tua funzione max (o qualsiasi altro nome già definito nello spazio dei nomi std)
Chewy Gumball

1
Significa solo che dovresti stare attento con le usingdirettive perché in questo caso si spezzerebbe la tua funzione max () se ne avessi definita una e incluso <algorithm>. Questo è un caso semplice ma non sai mai cosa potresti rompere. Dovresti conoscere l'intera libreria per essere sicuro di non averlo rotto, ma non puoi sapere se il tuo codice si romperà (cioè la collisione del nome) in futuro.
ApplePie,

6

Citando il link fornito:

È possibile utilizzare una dichiarazione di utilizzo ovunque in un file .cc e in funzioni, metodi o classi nei file .h.

// OK nei file .cc.

// Deve essere in una funzione, metodo o classe nei file .h.

usando :: foo :: bar;

Lo stile di Google ti proibisce di importare spazi dei nomi in un contesto globale, ma ti permette di farlo in quelli locali.

Ovunque in cui l'utilizzo della dichiarazione abbia effetto solo su una parte limitata e chiaramente visibile del codice, è perfettamente accettabile.

Quando inquini il contesto globale, il codice non correlato viene influenzato (implicitamente usando l'intestazione). Non succede nulla quando lo fai nel contesto locale.


Abbiamo gli stessi standard. Alcune delle nostre persone digiteranno localmente un lungo spazio dei nomi. es. typedef foolicious :: barlicious fb; fb :: drink d;
Michael Mathews,

1

non raccomandano di usare lo spazio dei nomi usando ornamespace: function` - se non l'ho interpretato male.

L'hai fatto. La raccomandazione si applica solo alla using namespacedirettiva (che viene comunemente definita abusing namespace, non interamente umoristica). Si consiglia vivamente di utilizzare il nome completo di una funzione o di un oggetto, ad esempio std::cout.


1

Sebbene la domanda abbia già delle risposte utili, un dettaglio sembra non essere sufficientemente breve.

La maggior parte dei programmatori è inizialmente un po 'confuso con la usingparola chiave e le descrizioni d' namespaceuso, anche se cercano di impararla cercando il riferimento, poiché dichiarazione e direttiva sono piuttosto equivalenti, entrambe sono parole lunghe relativamente astratte che iniziano con d .

Gli identificatori all'interno degli spazi dei nomi sono accessibili nominando esplicitamente lo spazio dei nomi:

myNamespace::myIdent

questo può essere molto più chiavi da digitare. Ma può anche ridurre il significato del codice, se la maggior parte degli identificatori ha il prefisso allo stesso modo. La usingparola chiave aiuta a prevenire questi svantaggi dello spazio dei nomi. Dato che usingfunziona a livello di compilatore (non è una macro), il suo effetto dura per l'intero ambito in cui viene utilizzato. Ecco perché lo stile di Google limita il suo utilizzo a ambiti ben definiti, ad esempio classi nei file di intestazione o funzioni nei file cpp.

... ovviamente c'è una differenza tra l' utilizzo della dichiarazione

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

e usando la direttiva

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

Se utilizzato in ambiti enormi, quest'ultimo porta a molta più confusione.


1

Ecco qui:

#include <iostream>

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

Scrivendolo in questo modo, evitiamo un ADL soggetto a errori insieme all'uso di direttive e dichiarazioni.

Questa dovrebbe essere una risposta sarcastica. :-D

Sono con Herb Sutter su Google su questo. Dagli standard di codifica C ++:

Puoi e dovresti usare lo spazio dei nomi usando dichiarazioni e direttive liberamente nei tuoi file di implementazione dopo le direttive #include e sentirti bene. Nonostante ripetute affermazioni contrarie, lo spazio dei nomi che utilizza dichiarazioni e direttive non è malvagio e non vanifica lo scopo degli spazi dei nomi. Piuttosto, sono ciò che rende utilizzabili gli spazi dei nomi .

Puoi essere ossessionato da potenziali conflitti nello spazio dei nomi che probabilmente non si manifesteranno mai e probabilmente non saranno difficili da risolvere in un evento così astronomicamente raro evitando attentamente le usingdirettive e specificando esplicitamente ogni cosa che usi (fino agli operatori) con le usingdichiarazioni, oppure vai avanti e inizia using namespace std. Raccomando quest'ultimo dal punto di vista della produttività.

La maggior parte dei libri di testo in c ++ insegna ai principianti usando lo spazio dei nomi std; stanno propagando una cattiva pratica di programmazione?

Al contrario, se me lo chiedi, e credo che Sutter sia d'accordo.

Ora, nel corso della mia carriera, ho riscontrato un totale di circa 3 conflitti usingnello spazio dei nomi come risultato diretto delle direttive in basi di codice che coprono decine di milioni di LOC. Tuttavia, in tutti e 3 i casi, erano in file di origine che si estendevano su oltre 50.000 righe di codice legacy, originariamente scritto in C e poi bastardato in C ++, eseguendo un enorme elenco eclettico di funzioni disparate, tra cui intestazioni di una dozzina di librerie diverse, e avendo un elenco epico #includesche si estendeva su una pagina. Nonostante il caos epico, non sono stati troppo difficili da correggere poiché hanno causato errori di compilazione su OSX (l'unico sistema operativo in cui il codice non è stato creato), non i bug di runtime. Non organizzare il codice in questo modo da incubo e dovresti andare bene.

Detto questo, evita sia le using direttive che le dichiarazioni nei file di intestazione. È semplicemente ritardato. Ma per i file di origine, e in particolare quelli che non hanno un'intera pagina piena di #includedirettive, direi di non sudare se non lavori per Google.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.