Perché "utilizza lo spazio dei nomi std;" considerata cattiva pratica?


2641

Mi è stato detto da altri che scrivere using namespace std;nel codice è sbagliato e che invece dovrei usare std::coute std::cindirettamente.

Perché è using namespace std;considerata una cattiva pratica? È inefficiente o rischia di dichiarare variabili ambigue (variabili che condividono lo stesso nome di una funzione nello stdspazio dei nomi)? Incide sulle prestazioni?


512
Non dimenticare che puoi fare: "usando std :: cout;" il che significa che non è necessario digitare std :: cout, ma non portare contemporaneamente l'intero spazio dei nomi std.
Bill

2

64
È particolarmente dannoso utilizzare "using namespace std" nell'ambito dei file header. Usarlo nei file sorgente (* .cpp) nell'ambito dei file dopo tutto include non è così male, poiché il suo effetto è limitato a una singola unità di traduzione. Ancora meno problematico lo sta usando all'interno di funzioni o classi, perché il suo effetto è limitato all'ambito della funzione o della classe.
sh-

5
Vorrei scoraggiare usare direttiva using ma per i namespace specifico come std::literals::chrono_literals, Poco::Data:Keywords, Poco::Unitse roba che si occuperà di letterali o trucchi di leggibilità. Ogni volta che si trova nell'intestazione o nei file di implementazione. Potrebbe essere OK in un ambito di funzioni immagino, ma a parte i letterali e cose del genere, non è utile.
Ludovic Zenohate Lagouardette,

7
@Jon: Non ha nulla a che fare con lo spazio dei nomi in particolare. La mia enfasi doveva essere "nell'ambito del file nei file di intestazione". Per dirlo come un consiglio: non utilizzare "using namespace" (std o altro) nell'ambito dei file di intestazione. Va bene usarlo nei file di implementazione. Scusa per l'ambiguità.
sh-

Risposte:


2231

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 e puoi chiamare Blah()da Foo e Quux()da Bar senza problemi. Ma un giorno esegui l'aggiornamento a una nuova versione di Foo 2.0, che ora offre una funzione chiamata Quux(). Ora hai un conflitto: sia Foo 2.0 che Bar vengono importati Quux()nel tuo spazio dei nomi globale. Ci vorrà qualche sforzo per risolvere, specialmente se i parametri della funzione coincidono.

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


435
Mi è sempre piaciuto "importare big_honkin_name come bhn", quindi puoi semplicemente usare "bhn.something" piuttosto che "big_honkin_name.something" - riduce davvero la digitazione. Il C ++ ha qualcosa del genere?
paxdiablo,

764
@Pax namespace io = boost :: filesystem;
AraK,

152
Penso che sia esagerare le cose per dire che è "qualche sforzo per risolvere". Non avrai istanze del nuovo foo :: Quux, quindi disambigua tutti i tuoi usi attuali con bar :: Quux.
MattyT,

289
Qualcuno sensibile creerebbe una libreria con tipi il cui nome non qualificato si scontrerebbe con i tipi std?
erikkallen,

94
@ TomA: Il problema #defineè che non si limita agli spazi dei nomi, ma calpesta l'intera base di codice. Un alias dello spazio dei nomi è quello che vuoi.
sbi,

1391

Sono d'accordo con tutto ciò che Greg ha scritto , ma vorrei aggiungere: può anche peggiorare di quanto Greg abbia detto!

Library Foo 2.0 potrebbe introdurre una funzione, Quux()che è una corrispondenza inequivocabilmente migliore per alcune delle tue chiamate Quux()rispetto al bar::Quux()tuo codice chiamato per anni. Quindi il tuo codice viene ancora compilato , ma chiama silenziosamente la funzione sbagliata e fa sapere cosa. Questo è il più brutto possibile.

Tenete a mente che il stdnamespace ha tonnellate di identificatori, molti dei quali sono molto più comuni (si pensi list, sort, string,iterator , etc.) che sono molto suscettibili di apparire in altro codice, anche.

Se lo consideri improbabile: qui su Stack Overflow è stata posta una domanda in cui è accaduto praticamente esattamente questo (una funzione errata è stata chiamata a causa di un std::prefisso omesso ) circa un anno e mezzo dopo che ho dato questa risposta. Ecco un altro esempio più recente di una simile domanda. Quindi questo è un vero problema.


Ecco un altro punto dati: molti, molti anni fa, trovavo anche fastidioso dover aggiungere il prefisso a tutto dalla libreria standard std::. Quindi ho lavorato a un progetto in cui all'inizio è stato deciso che sia le usingdirettive che le dichiarazioni sono state bandite ad eccezione degli ambiti delle funzioni. Indovina un po? La maggior parte di noi ha impiegato pochissime settimane per abituarsi a scrivere il prefisso, e dopo alcune settimane la maggior parte di noi ha persino concordato sul fatto che rendesse il codice più leggibile . C'è una ragione per questo: se ti piace la prosa più o meno lunga è soggettiva, ma i prefissi aggiungono oggettivamente 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 ha avuto diversi milioni di righe di codice. Dato che queste discussioni si ripetono continuamente, una volta ero curioso di sapere quanto spesso l'ambito della funzione (consentita) usingfosse effettivamente utilizzato nel progetto. Ho cercato le fonti e ho trovato solo una o due dozzine di posti in cui è stato utilizzato. Per me questo indica che, una volta provato, gli sviluppatori non trovano std::abbastanza doloroso impiegare l'uso delle direttive anche una volta ogni 100 kLoC anche dove è stato permesso di usarle.


Bottom line: prefissare esplicitamente tutto non fa alcun danno, richiede pochissimo per abituarsi e presenta vantaggi oggettivi. In particolare, facilita l'interpretazione del codice da parte del compilatore e dei lettori umani, e questo dovrebbe probabilmente essere l'obiettivo principale durante la scrittura del codice.


140
Danneggia in modo significativo la densità del codice che è possibile comprimere in una singola riga. Finisci per scrivere il tuo codice in un modo molto lungo; che riduce la leggibilità. Personalmente, penso che il codice più breve (ma non troppo breve) tende ad essere più leggibile (poiché ci sono meno cose da leggere e meno cose da distrarre).
Lie Ryan,

92
Immagino che ti sei perso ai vecchi tempi prima che il C ++ avesse una stringclasse standard , e apparentemente ogni biblioteca aveva la sua. Ti dico cosa: continueremo a scrivere il nostro codice std::e potrai eseguirlo grep -v std:: | vimmentre lo navighi. Oppure puoi insegnare al tuo editor che std::è una parola chiave che deve essere colorata come il colore di sfondo. Basta che funzioni.
Mike DeSimone,

80
Non penso affatto std::dannoso. Fornisce informazioni molto importanti (vale a dire "tutto ciò che viene dopo fa parte della libreria standard", ed è ancora un prefisso piuttosto breve e compatto. Il più delle volte, non è affatto un problema. A volte, hai alcune righe di codice dove è necessario fare molto riferimento a simboli specifici nello stdspazio dei nomi, e quindi usingun'istruzione in quel particolare ambito risolve il problema in modo corretto, ma nel caso generale non è rumore, trasmette informazioni preziose oltre a rimuovere le ambiguità
jalf

147
Ogni volta che vedo std::, so che verrà da std::senza doverci pensare. Se vedo stringo listo mapda solo, mi chiedo un po '.
Mateen Ulhaq,

68
@LieRyan Allora buona fortuna a scrivere una libreria di geometrie senza mai nominare qualcosa vector, transformo distance. E questi sono solo esempi dei molti nomi molto comuni usati nella libreria standard. Suggerire di non usarli per paura o un'opinione distorta della funzionalità dello spazio dei nomi che è parte integrante di C ++ è piuttosto controproducente.
Christian Rau,

420

Il problema con il mettere using namespace dei file di intestazione delle tue classi è che costringe chiunque voglia usare le tue classi (includendo i tuoi file di intestazione) a "usare" (cioè vedere tutto in) quegli altri spazi dei nomi.

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


Attenzione che alcune persone non sono d'accordo con il mio dire "sentiti libero" in questo modo - perché sebbene usingun'istruzione in un file cpp sia migliore che in un'intestazione (perché non influisce sulle persone che includono il tuo file di intestazione), pensano che non sia ancora buono (perché a seconda del codice potrebbe rendere più difficile mantenere l'implementazione della classe). Questa voce Super-FAQ C ++ dice:

La direttiva using esiste per il codice C ++ legacy e per facilitare la transizione agli spazi dei nomi, ma probabilmente non dovresti usarlo su base regolare, almeno non nel tuo nuovo codice C ++.

Le FAQ suggeriscono due alternative:

  • Una dichiarazione di utilizzo:

    using std::cout; // a using-declaration lets you use cout without qualification
    cout << "Values:";
  • Basta digitare std ::

    std::cout << "Values:";

1
Ovviamente non dovresti mai assumere lo stato della cout globale, per timore che qualcuno abbia std: cout << std :: hex e non sia riuscito a std :: restore_cout_state in seguito. Ma questo è un altro fatberg.
Móż

233

Di recente ho riscontrato un reclamo su Visual Studio 2010 . Si è scoperto che praticamente tutti i file di origine avevano queste due righe:

using namespace std;
using namespace boost;

Molte funzionalità di Boost stanno entrando nello standard C ++ 0x e Visual Studio 2010 ha molte funzionalità C ++ 0x, quindi all'improvviso questi programmi non si sono compilati.

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


14
Questo. Boost e std hanno molte sovrapposizioni, specialmente da C ++ 11.
einpoklum,

1
L'ho fatto una volta e ho imparato una lezione nel modo più duro. Ora non uso mai usingal di fuori di una definizione di funzione e raramente uso using namespaceaffatto.
Ferruccio,

210

Versione breve: non utilizzare usingdichiarazioni o direttive globali nei file di intestazione. Sentiti libero di usarli nei file di implementazione. Ecco cosa hanno da dire Herb Sutter e Andrei Alexandrescu su questo problema negli standard di codifica C ++ (il grassetto per l'enfasi è mio):

Sommario

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

Corollario: nei file di intestazione, non scrivere a livello di spazio dei nomi usando le direttive o usando le dichiarazioni; invece, esplicitamente qualificare lo spazio dei nomi per tutti i nomi. (La seconda regola segue la prima, perché le intestazioni non possono mai sapere quali altre intestazioni #include potrebbero apparire dopo di esse.)

Discussione

In breve: 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 .


4
Solo un'altra opinione del programmatore qui, ma mentre sono d'accordo al 100% con l'affermazione che la parola usingnon dovrebbe mai apparire in un'intestazione, non sono così convinto della licenza gratuita da collocare using namespace xyz;ovunque nel tuo codice, specialmente se lo xyzè std. Uso il using std::vector;modulo, dal momento che trascina solo un singolo elemento dallo spazio dei nomi in ambito pseudo-globale, portando quindi a un rischio molto minore di una collisione.
Dgnuff,

2
@Lightness Races in Orbit hai ovviamente diritto alla tua opinione. Sarebbe stato più utile se ci fosse stato qualche tentativo di spiegazione del perché non sei d'accordo con i consigli forniti in questa risposta. Soprattutto sarebbe interessante capire qual è il punto degli spazi dei nomi se "usarli" è male? Perché non semplicemente nominare le cose std_cout invece di std :: cout ... i creatori di C ++ / namespace devono aver avuto qualche idea quando si sono presi la briga di crearle.
nyholku,

1
@nyholku: Non c'è bisogno - la maggior parte delle altre risposte fornisce le stesse ragioni che vorrei. Inoltre, non esitare a notare il ":)" che ho aggiunto al mio commento! E che non ho detto che gli spazi dei nomi sono cattivi.
Razze di leggerezza in orbita,

Sì, l'ho notato :) ma IMO la maggior parte delle risposte (che vanno contro questo saggio consiglio) sono sbagliate (non che io abbia fatto statistiche quale sia la maggioranza ora). Se accetti che lo spazio dei nomi non sia "cattivo", potresti dire dove pensi che siano appropriati se non sei d'accordo con questa risposta?
nyholku,

Non posso fare a meno di sentire che il using namespacemale gotoè come il male. Entrambi hanno usi validi, ma 999 volte su 1000 verranno utilizzati in modo errato. Quindi, sì, con using namespacenella fonte non inquinerai lo spazio dei nomi di altre inclusioni, pulito. Ma non ti proteggerà ancora dal "divertimento" che deriva da using namespace Foo+ using namespace Barcon la tua chiamata (Foo implicito: :) baz(xyz)e improvvisamente la violazione del codice (senza modifiche correlate) solo perché perché è Bar::baz()stato aggiunto da qualche parte, che sembra essere un migliore match (e quindi ora viene chiamato invece)
CharonX

122

Non si dovrebbe usare la usingdirettiva nell'ambito 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));
}

È meglio della qualifica esplicita ( std::sin, std::cos...), perché è più breve e ha la capacità di lavorare con tipi in virgola mobile definiti dall'utente (tramite ricerca dipendente dall'argomento (ADL)).


9
Mi dispiace, ma non sono assolutamente d'accordo con questo.
Billy ONeal,

4
@Billy: non esiste altro modo per supportare la chiamata userlib :: cos (userlib :: superint). Ogni funzione ha un uso.
Zan Lynx,

17
@Zan: Certo che c'è. using std::cos;, using std::sinecc. Il problema però è che qualsiasi progetto ben progettato userlibavrà anche il proprio sine cosall'interno del proprio spazio dei nomi, quindi questo non ti aiuta davvero. (A meno che non ci sia un using namespace userlibmodello precedente e questo è altrettanto negativo di using namespace std- e l'ambito non è limitato.) Inoltre, l'unica funzione come questa che io abbia mai visto accadere è swap, e in questi casi consiglierei di creare solo un modello specializzazione di std::swaped evitare l'intero problema.
Billy ONeal,

11
@BillyONeal: template<typename T> void swap(MyContainer<T>&, MyContainer<T>&)(Non esiste una specializzazione parziale del modello di funzione (FTPS), quindi a volte è necessario ricorrere al sovraccarico.
sbi,

38
@BillyONeal: Il tuo commento (votato 7 volte!) È sbagliato: la situazione che descrivi è esattamente ciò che ADL è stato progettato per coprire. In breve, se xha uno o più "spazi dei nomi associati" (ad esempio, se è stato definito in namespace userlib) allora ogni chiamata di funzione che assomiglia cos(x)sarà inoltre guardare in quegli spazi - senza alcun using namespace userlib;anticipo sia necessario. Zan Lynx ha ragione (e la ricerca del nome C ++ è bizantina ...)
j_random_hacker

97

Non usarlo a livello globale

È considerato "cattivo" solo se utilizzato a livello globale . Perché:

  • Si ingombra lo spazio dei nomi in cui si sta programmando.
  • I lettori avranno difficoltà a vedere da dove proviene un particolare identificatore, quando ne usi molti using namespace xyz.
  • Qualunque cosa sia vera per gli altri lettori del tuo codice sorgente è ancora più vera per il lettore più frequente: te stesso. Torna tra un anno o due e dai un'occhiata ...
  • Se parli solo di using namespace stdte potresti non essere a conoscenza di tutte le cose che prendi - e quando ne aggiungi un'altra #includeo passi a una nuova revisione C ++ potresti avere conflitti di nomi di cui non eri a conoscenza.

Puoi usarlo localmente

Vai avanti e usalo localmente (quasi) liberamente. Questo, ovviamente, ti impedisce di ripeterestd:: - e anche la ripetizione è cattiva.

Un linguaggio per usarlo localmente

In C ++ 03 c'era un linguaggio - codice boilerplate - per implementare una swapfunzione per le tue classi. È stato suggerito di utilizzare effettivamente un locale using namespace std- o almeno 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::swapfor value_, ovverovoid std::swap(int, int) .
  • Se hai un sovraccarico void swap(Child&, Child&) implementato il compilatore lo sceglierà.
  • Se non si dispone di tale sovraccarico, il compilatore utilizzerà void std::swap(Child&,Child&)e farà del suo meglio per scambiarli.

Con C ++ 11 non vi è alcun motivo per utilizzare più questo modello. L'implementazione di è std::swapstata modificata per trovare un potenziale sovraccarico e sceglierlo.


5
"L'implementazione di std :: swap è stata modificata per trovare un potenziale sovraccarico e sceglierlo." - Che cosa? Sei sicuro di questo? Anche se è vero che fornire un'abitudine swapin primo luogo non è più così importante in C ++ 11, poiché lo std::swapstesso è più flessibile (usa la semantica di spostamento). Ma std::swapscegliere automaticamente il tuo scambio personalizzato, questo è assolutamente nuovo per me (e non ci credo davvero).
Christian Rau

@ChristianRau Penso di sì, sì. L'ho letto su SO da qualche parte. Possiamo sempre chiedere a Howard , dovrebbe saperlo. Sto scavando e scavando ora ...
towi

14
Anche nel caso dello scambio, il linguaggio più chiaro (e per fortuna più comune) è scrivere using std::swap;piuttosto che using namespace std;. Il linguaggio più specifico ha meno effetti collaterali e quindi rende il codice più gestibile.
Adrian McCarthy,

11
L'ultima frase è sbagliata. In C ++ 11 lo Std Swap Two Step è stato ufficialmente benedetto come il modo giusto di chiamare swap, e vari altri posti nello standard sono stati cambiati per dire che chiamano swapcosì (NB come detto sopra, using std::swapè il modo giusto, non using namespace std). Ma std::swapin sé era decisamente non è cambiato di trovare qualche altro swape usarlo. Se std::swapviene chiamato, std::swapviene utilizzato.
Jonathan Wakely,

3
using std::swapTuttavia, potrebbe essere più saggio digitare solo localmente, per ridurre lo spazio dei nomi locale e allo stesso tempo creare codice auto-documentante. Raramente sei mai interessato all'intero spazio dei nomi std, quindi appena fuori scegli le parti che ti interessano.
Lundin,

79

Se si importano i file di intestazione di destra improvvisamente hanno nomi come hex, left, pluso countnel vostro ambito globale. Questo potrebbe essere sorprendente se non si è consapevoli del fatto che std::contenga questi nomi. Se provi anche a usare questi nomi localmente, puoi creare confusione.

Se tutto il materiale standard si trova nel suo spazio dei nomi, non devi preoccuparti delle collisioni di nomi con il tuo codice o altre librerie.


12
+1 per non parlare distance. preferisco comunque i nomi non qualificati laddove praticamente possibile, poiché ciò aumenta la leggibilità per me. inoltre, penso che il fatto che di solito non qualifichiamo le cose nel discorso orale e che siamo disposti a dedicare tempo a risolvere possibili ambiguità, significa che ha valore essere in grado di capire di cosa si sta parlando senza qualifiche e applicato alla fonte codice significa che è strutturato in modo tale che sia chiaro di cosa si tratta anche senza qualifiche.
Saluti e hth. - Alf,

Ad essere onesti, però, non ne hai la maggior parte se non includi <iomanip>. Comunque, buon punto.
einpoklum,

48

Un altro motivo è la sorpresa.

Se vedo cout << blah, invece di std::cout << blahpensare: che cos'è questo cout? È normale cout? È qualcosa di speciale?


25
È uno scherzo? Davvero non posso dirlo. In caso contrario, personalmente presumo che sia il normale "cout" a meno che non ti fidi del codice poiché altrimenti sarebbe un odore di codice OLTRE MAGGIORE, IMO. ... E se non ti fidi del codice, allora perché lo stai usando? Nota che non sto dicendo "FIDUCIA EVERYThING !!" ma questo sembra anche un po 'inverosimile se, ad esempio, hai a che fare con una libreria ben nota di GitHub o qualcosa del genere.
Brent Rittenhouse,

28
@BrentRittenhouse coutè un cattivo esempio perché tutti lo riconoscono. Ma immagina futurein un'app finanziaria. È un contratto per comprare o vendere qualcosa a una data specifica? No non lo è. Se il codice dicesse std::futureche non saresti così facilmente confuso.
James Hollis,

2
@BrentRittenhouse forse un piccolo esempio negativo, ci sono almeno quattro diverse librerie che hanno cout. Può essere "è una libreria standard? Libstdc ++? Stl? Qualcos'altro?" E no, non tutti sanno std :: cout, almeno intrinsecamente, 6 dei 7 nuovi lavoratori che riceviamo non lo fanno. Perché i curricula dell'educazione non usano quelli dell'educazione. Devo scacciare printfs. O debug () - da Qt.
Swift - Friday Pie,

1
Veramente? È praticamente nel primo esempio del primo capitolo di tanti libri su C ++, se non altro (con l'utilizzo dell'operatore di inserimento) è l' unico C ++ che alcuni nuovi corpi conoscono.
mckenzm,

@mckenzm Potrei metterlo in un libro o negli appunti di una lezione per ridurre il disordine, ma non nel codice
Martin Beckett,

45

I programmatori esperti usano tutto ciò che risolve i loro problemi ed evitano qualsiasi cosa crei nuovi problemi, ed evitano le direttive d'uso a livello di file di intestazione per questo motivo esatto.

I programmatori esperti cercano anche di evitare la piena qualificazione dei nomi nei loro file sorgente. Un motivo secondario per questo è che non è elegante scrivere più codice quando è sufficiente meno codice a meno che non ci siano buone ragioni . Uno dei motivi principali di ciò è la disattivazione della ricerca dipendente dall'argomento (ADL).

Quali sono questi buoni motivi ? A volte i programmatori vogliono esplicitamente disattivare ADL, altre volte vogliono disambiguare.

Quindi i seguenti sono OK:

  1. Direttive di utilizzo a livello di funzione e dichiarazioni di utilizzo all'interno delle implementazioni delle funzioni
  2. Dichiarazioni di utilizzo a livello di file di origine all'interno dei file di origine
  3. (A volte) direttive di utilizzo a livello di file di origine

43

Sono d'accordo che non dovrebbe essere usato a livello globale, ma non è così male da usare localmente, come in a namespace. Ecco un esempio da "Il linguaggio di programmazione C ++" :

namespace My_lib {

    using namespace His_lib; // Everything from His_lib
    using namespace Her_lib; // Everything from Her_lib

    using His_lib::String; // Resolve potential clash in favor of His_lib
    using Her_lib::Vector; // Resolve potential clash in favor of Her_lib

}

In questo esempio, abbiamo risolto potenziali conflitti di nomi e ambiguità derivanti dalla loro composizione.

I nomi esplicitamente dichiarati lì (compresi i nomi dichiarati da using-dichiarations like His_lib::String) hanno la priorità sui nomi resi accessibili in un altro ambito da una using-direttiva ( using namespace Her_lib).


29

Lo considero anche una cattiva pratica. Perché? Solo un giorno ho pensato che la funzione di uno spazio dei nomi è quella di dividere le cose, quindi non dovrei rovinarlo con il lancio di tutto in una borsa globale.

Tuttavia, se uso spesso 'cout' e 'cin', scrivo: using std::cout; using std::cin;nel file .cpp (mai nel file di intestazione mentre si propaga #include). Penso che nessuno sano di mente nominerà mai un flusso couto cin. ;)


7
Questa è una dichiarazione di utilizzo locale , una cosa molto diversa da una direttiva di utilizzo .
sbi,

25

È bello vedere il codice e sapere cosa fa. Se vedo std::coutso che è il coutflusso della stdlibreria. Se vedo, coutallora non lo so. Esso potrebbe essere il coutflusso della stdbiblioteca. Oppure potrebbero esserci int cout = 0;dieci righe in più nella stessa funzione. O una staticvariabile denominata coutin quel file. Potrebbe essere qualsiasi cosa.

Ora prendi un milione di righe di codice di base, che non è particolarmente grande, e stai cercando un bug, il che significa che sai che c'è una riga in questo milione di righe che non fa quello che dovrebbe fare. cout << 1;potrebbe leggere un static intnome cout, spostarlo a sinistra di un po 'e buttare via il risultato. In cerca di un bug, dovrei controllarlo. Riesci a vedere come preferisco davvero vedere std::cout?

È una di queste cose che sembrano un'ottima idea se sei un insegnante e non hai mai dovuto scrivere e mantenere alcun codice per vivere. Adoro vedere il codice in cui (1) so cosa fa; e, (2) sono fiducioso che la persona che lo scrive sapesse cosa fa.


4
Come fai a sapere "std :: cout << 1" non sta leggendo un int statico chiamato cout nello spazio dei nomi std spostandolo di uno e gettando via il risultato? Inoltre, come fai a sapere cosa fa "<<";) ??? ... sembra che questa risposta non sia un buon punto dati per evitare di "usare".
nyholku,

4
Se qualcuno ha ridefinito std :: cout in modo che sia un numero intero, allora il tuo problema non è tecnico, ma sociale: qualcuno ce l'ha dentro. (e probabilmente dovresti anche controllare tutte le intestazioni per cose come #define true false, ecc.)
Jeremy Friesner

2
Quando vedo cout so che è sempre std :: cout. Se sbaglio, è un problema della persona che ha scritto questo codice, non io :)
Tien Do

22

Si tratta di gestire la complessità. L'uso dello spazio dei nomi tirerà le cose che non vuoi, e quindi probabilmente renderà più difficile il debug (dico forse). L'uso di std :: ovunque è più difficile da leggere (più testo e tutto il resto).

Cavalli per i corsi - gestisci la tua complessità come meglio puoi e ti senti capace.


18

Prendere in considerazione

// myHeader.h
#include <sstream>
using namespace std;


// someoneElses.cpp/h
#include "myHeader.h"

class stringstream {  // Uh oh
};

Si noti che questo è un semplice esempio. Se hai file con 20 inclusioni e altre importazioni, avrai un sacco di dipendenze da percorrere per capire il problema. La cosa peggiore è che puoi ottenere errori non correlati in altri moduli a seconda delle definizioni in conflitto.

Non è orribile, ma ti risparmierai mal di testa non utilizzandolo nei file di intestazione o nello spazio dei nomi globale. Probabilmente va bene farlo in ambiti molto limitati, ma non ho mai avuto problemi a digitare i cinque caratteri extra per chiarire da dove provengono le mie funzioni.


18
  1. Devi essere in grado di leggere il codice scritto da persone che hanno opinioni di stile e best practice diverse da te.

  2. Se stai solo usando cout, nessuno viene confuso. Ma quando hai molti spazi dei nomi che volano in giro e vedi questa classe e non sei esattamente sicuro di cosa faccia, avere lo spazio dei nomi esplicito agisce come un commento di sorta. A prima vista puoi vedere "oh, questa è un'operazione di filesystem" o "sta facendo cose di rete".


17

Usare molti spazi dei nomi allo stesso tempo è ovviamente una ricetta per il disastro, ma usare lo spazio dei nomi JUST stde solo lo spazio dei nomi stdnon è un grosso problema secondo me perché la ridefinizione può avvenire solo con il tuo codice ...

Quindi considerali come funzioni riservate come "int" o "class" e basta.

Le persone dovrebbero smettere di essere così anali al riguardo. Il tuo insegnante aveva sempre ragione. Usa ONE ONE namespace; questo è il punto centrale dell'uso degli spazi dei nomi al primo posto. Non dovresti usarne più di uno contemporaneamente. A meno che non sia tuo. Quindi, di nuovo, la ridefinizione non accadrà.


Creare collisioni non è poi così difficile - stringhe brevi come min, endeless appaiono nel std::namespace. Ma più, ora che std::contiene migliaia di simboli, è utile che il lettore sappia da dove proviene un nuovo simbolo che potrebbero non conoscere.
Tom Swirly,

Lo spazio dei nomi standard esiste perché le persone, tu, i tuoi colleghi o le persone che scrivono middleware che usi, non sono sempre sagge nel mettere le funzioni all'interno degli spazi dei nomi. Quindi puoi importare tutto lo std :: e nient'altro, mentre invoca ancora una collisione tra, diciamo, std :: min e l'eredità di qualcun altro :: min () da prima del tempo in cui era in std.
Aiken Drum,

14

Sono d'accordo con gli altri qui, ma vorrei affrontare le preoccupazioni relative alla leggibilità: puoi evitare tutto ciò semplicemente usando typedefs nella parte superiore del tuo file, funzione o dichiarazione di classe.

Di solito lo uso nella mia dichiarazione di classe poiché i metodi in una classe tendono a trattare tipi di dati simili (i membri) e un typedef è un'opportunità per assegnare un nome che sia significativo nel contesto della classe. Questo in realtà aiuta la leggibilità nelle definizioni dei metodi di classe.

// Header
class File
{
   typedef std::vector<std::string> Lines;
   Lines ReadLines();
}

e nell'attuazione:

// .cpp
Lines File::ReadLines()
{
    Lines lines;
    // Get them...
    return lines;
}

al contrario di:

// .cpp
vector<string> File::ReadLines()
{
    vector<string> lines;
    // Get them...
    return lines;
}

o:

// .cpp
std::vector<std::string> File::ReadLines()
{
    std::vector<std::string> lines;
    // Get them...
    return lines;
}

Solo un piccolo commento, mentre typedef è utile prenderei in considerazione l'idea di creare una classe che rappresenti Lines invece di usare typedef.
Eyal Solnik,

14

Un esempio concreto per chiarire la preoccupazione. Immagina di avere una situazione in cui hai due librerie fooe bar, ognuna con il proprio spazio dei nomi:

namespace foo {
    void a(float) { /* Does something */ }
}

namespace bar {
    ...
}

Ora diciamo che usi fooe barinsieme nel tuo programma come segue:

using namespace foo;
using namespace bar;

void main() {
    a(42);
}

A questo punto va tutto bene. Quando esegui il tuo programma, "Fa qualcosa". Ma più tardi aggiorni bare diciamo che è cambiato per essere come:

namespace bar {
    void a(float) { /* Does something completely different */ }
}

A questo punto otterrai un errore del compilatore:

using namespace foo;
using namespace bar;

void main() {
    a(42);  // error: call to 'a' is ambiguous, should be foo::a(42)
}

Quindi dovrai fare un po 'di manutenzione per chiarire che "a" significava foo::a. Questo è indesiderabile, ma per fortuna è abbastanza facile (basta aggiungere foo::davanti a tutte le chiamate aa che il compilatore contrassegna come ambiguo).

Ma immagina uno scenario alternativo in cui invece la barra è cambiata per assomigliare a questa:

namespace bar {
    void a(int) { /* Does something completely different */ }
}

A questo punto la tua chiamata a(42)all'improvviso si lega a bar::ainvece di foo::ae invece di fare "qualcosa" fa "qualcosa di completamente diverso". Nessun avviso del compilatore o altro. Il tuo programma inizia silenziosamente a fare qualcosa di completamente diverso da prima.

Quando usi uno spazio dei nomi stai rischiando uno scenario come questo, motivo per cui le persone si sentono a disagio nell'utilizzare gli spazi dei nomi. Più sono le cose in uno spazio dei nomi, maggiore è il rischio di conflitto, quindi le persone potrebbero essere ancora più a disagio nell'utilizzare lo spazio dei nomi std(a causa del numero di cose in quello spazio dei nomi) rispetto ad altri spazi dei nomi.

In definitiva questo è un compromesso tra scrivibilità vs affidabilità / manutenibilità. Anche la leggibilità può essere un fattore determinante, ma ho potuto vedere gli argomenti per quello andando in entrambi i modi. Normalmente direi che l'affidabilità e la manutenibilità sono più importanti, ma in questo caso pagherai costantemente il costo della scrivibilità per un impatto di affidabilità / manutenibilità piuttosto raro. Il "miglior" compromesso determinerà il tuo progetto e le tue priorità.


Il secondo scenario conferma l'accordo per me. Ancora nessuno spazio dei nomi. Non è possibile che cambiamenti così sottili nella funzionalità non vengano rilevati sotto il cofano.
safe_malloc,

13

Uno spazio dei nomi è un ambito denominato. Gli spazi dei nomi vengono utilizzati per raggruppare le dichiarazioni correlate e per tenere separati gli elementi separati. Ad esempio, due librerie sviluppate separatamente possono utilizzare lo stesso nome per fare riferimento a elementi diversi, ma un utente può comunque utilizzare entrambi:

namespace Mylib{
    template<class T> class Stack{ /* ... */ };
    // ...
}

namespace Yourlib{
    class Stack{ /* ... */ };
    // ...
}

void f(int max) {
    Mylib::Stack<int> s1(max); // Use my stack
    Yourlib::Stack    s2(max); // Use your stack
    // ...
}

La ripetizione di un nome di spazio dei nomi può essere una distrazione sia per i lettori che per gli scrittori. Di conseguenza, è possibile affermare che i nomi di un determinato spazio dei nomi sono disponibili senza qualifica esplicita. Per esempio:

void f(int max) {
    using namespace Mylib; // Make names from Mylib accessible
    Stack<int> s1(max); // Use my stack
    Yourlib::Stack s2(max); // Use your stack
    // ...
}

I namespace forniscono un potente strumento per la gestione di diverse librerie e di diverse versioni di codice. In particolare, offrono al programmatore alternative su come esplicito fare un riferimento a un nome non locale.

Fonte: una panoramica del linguaggio di programmazione C ++ di Bjarne Stroustrup


4
Molto interessante il fatto che questa risposta si basi sulla guida di nessun altro che Bjarne Stroustrup ha guadagnato -2 ... il ragazzo Bjarne deve essere stato un programmatore povero e inesperto quando ha introdotto questa funzionalità in C ++
nyholku l'

@nyholku: guarda questo .
sabato

10

Un esempio in cui using namespace stdgenera un errore di compilazione a causa dell'ambiguità di count, che è anche una funzione nella libreria degli algoritmi.

#include <iostream>

using namespace std;

int count = 1;
int main() {
    cout << count << endl;
}

2
::count--problema risolto. Di solito avrai più cose dal namespace std che da altrove, ergo mantenendo la direttiva using namespace potrebbe salvarti la digitazione.
PSkocik

Il vero problema qui è che C ++ ha ancora globi senza namespace. Questo, e il fatto che "questo" sia implicito nei metodi, causa così tanti bug e problemi che non riesco nemmeno a contarli, anche con la giusta variabile "count". ;)
Aiken Drum,

9

Non peggiora le prestazioni del tuo software o progetto. L'inclusione dello spazio dei nomi all'inizio del codice sorgente non è male. L'inclusione delle using namespace stdistruzioni varia in base alle tue esigenze e al modo in cui stai sviluppando il software o il progetto.

Il namespace stdcontiene le funzioni e le variabili standard del C ++. Questo spazio dei nomi è utile quando si usano spesso le funzioni standard C ++.

Come menzionato in questa pagina :

L'istruzione che utilizza lo spazio dei nomi std è generalmente considerata una cattiva pratica. L'alternativa a questa affermazione è specificare lo spazio dei nomi a cui appartiene l'identificatore usando l'operatore scope (: :) ogni volta che dichiariamo un tipo.

E vedi questa opinione :

Non vi è alcun problema nell'utilizzo di "utilizzo dello spazio dei nomi std" nel file sorgente quando si fa ampio uso dello spazio dei nomi e si è certi che nulla si scontrerà.

Alcune persone hanno detto che è una cattiva pratica includere il using namespace std nei file di origine perché stai invocando da quello spazio dei nomi tutte le funzioni e le variabili. Quando si desidera definire una nuova funzione con lo stesso nome di un'altra funzione contenuta in essa, namespace stdsi sovraccaricherebbe la funzione e si potrebbero verificare problemi dovuti alla compilazione o all'esecuzione. Non verrà compilato o eseguito come previsto.

Come menzionato in questo pagina :

Sebbene l'istruzione ci salvi dal digitare std :: ogni volta che desideriamo accedere a una classe o un tipo definiti nello spazio dei nomi std, importa l'intero spazio dei nomi std nello spazio dei nomi corrente del programma. Facciamo alcuni esempi per capire perché questa potrebbe non essere una buona cosa

...

Ora in una fase successiva di sviluppo, desideriamo utilizzare un'altra versione di cout che è implementata in modo personalizzato in alcune librerie chiamate "pippo" (ad esempio)

...

Notate come c'è un'ambiguità, a quale libreria punta? Il compilatore può rilevare questo e non compilare il programma. Nel peggiore dei casi, il programma potrebbe ancora essere compilato ma chiamare la funzione sbagliata, poiché non abbiamo mai specificato a quale spazio dei nomi appartenesse l'identificatore.


7

Non penso che sia necessariamente una cattiva pratica in tutte le condizioni, ma devi stare attento quando lo usi. Se stai scrivendo una libreria, probabilmente dovresti usare gli operatori di risoluzione dell'ambito con lo spazio dei nomi per evitare che la tua libreria si faccia da testa con altre librerie. Per il codice a livello di applicazione, non vedo nulla di sbagliato in esso.


7

"Perché 'using namespace std;' considerato una cattiva pratica in C ++? "

In altre parole: perché digitare cinque caratteri extra è considerato scomodo da alcuni?

Considera ad esempio la scrittura di un software numerico. Perché dovrei anche considerare di inquinare il mio spazio dei nomi globale tagliando "std :: vector" generale a "vector" quando "vector" è uno dei concetti più importanti del dominio problematico?


19
Non sono solo 5 caratteri extra; i suoi 5 caratteri extra ogni volta che fai riferimento a qualsiasi tipo di oggetto nella libreria standard. Che, se stai usando molto la libreria standard, lo sarà spesso. Quindi sono più realisticamente migliaia di caratteri extra in un programma di dimensioni decenti. Presumibilmente la direttiva "utilizzo" è stata aggiunta alla lingua in modo che potesse essere utilizzata ...
Jeremy Friesner,

5
Non sono 5 caratteri aggiuntivi ogni volta, sono 5 caratteri e probabilmente un paio di clic del mouse per aprire un menu e fare un Trova e sostituisci nell'editor di tua scelta.
DaveWalley,

1
Leggibilità. cout << hex << setw(4) << i << endl;è più facile da leggere distd::cout << std::hex << std::setw(4) << i << std::endl;
oz1cz

16
E ancora peggio: std::map<std::string,std::pair<std::string,std::string>>è orribile rispetto a map<string,pair<string,string>>.
oz1cz,

4
È una buona pratica è quella di digitare i contenitori STL comunque, quindi std :: non c'è davvero importanza. E C ++ 11 ci ha portato la parola chiave auto che rende le cose ancora più facili quando ad esempio si usano iteratori.
juzzlin,

7

Sono d'accordo con gli altri - chiede scontro di nomi, ambiguità e poi il fatto è che è meno esplicito. Mentre posso vedere l'uso di using, la mia preferenza personale è di limitarlo. Vorrei anche considerare fortemente ciò che alcuni altri hanno sottolineato:

Se vuoi trovare un nome di funzione che potrebbe essere un nome abbastanza comune, ma vuoi trovarlo solo nello stdspazio dei nomi (o viceversa - vuoi cambiare tutte le chiamate che non sono nello spazio dei stdnomi X, nello spazio dei nomi , ...), allora come proponi di farlo?

Potresti scrivere un programma per farlo, ma non sarebbe meglio passare il tempo a lavorare sul tuo progetto piuttosto che scrivere un programma per mantenere il tuo progetto?

Personalmente, in realtà non mi dispiace il std::prefisso. Mi piace più l'aspetto che non averlo. Non so se sia perché è esplicito e mi dice "questo non è il mio codice ... sto usando la libreria standard" o se è qualcos'altro, ma penso che sia più bello. Questo potrebbe essere strano dato che solo recentemente sono entrato in C ++ (usato e faccio ancora C e altre lingue per molto più tempo e C è la mia lingua preferita di tutti i tempi, proprio sopra l'assemblaggio).

C'è un'altra cosa, sebbene sia in qualche modo correlata a quanto sopra e ciò che altri sottolineano. Anche se questa potrebbe essere una cattiva pratica, a volte mi riservo std::namela versione e il nome della libreria standard per l'implementazione specifica del programma. Sì, davvero questo potrebbe morderti e morderti duramente, ma tutto si riduce al fatto che ho iniziato questo progetto da zero e sono l'unico programmatore per questo. Esempio: sovraccarico std::stringe lo chiamo string. Ho aggiunte utili. L'ho fatto in parte a causa della mia tendenza C e Unix (+ Linux) verso i nomi minuscoli.

Oltre a ciò, puoi avere alias dello spazio dei nomi. Ecco un esempio di dove è utile a cui potrebbe non essere stato fatto riferimento. Uso lo standard C ++ 11 e in particolare con libstdc ++. Bene, non ha std::regexsupporto completo . Certo, si compila, ma genera un'eccezione sulla falsariga di essere un errore alla fine del programmatore. Ma è mancanza di implementazione.

Quindi ecco come l'ho risolto. Installa il regex di Boost e collegalo. Quindi, faccio quanto segue in modo che quando libstdc ++ è stato implementato completamente, devo solo rimuovere questo blocco e il codice rimane lo stesso:

namespace std
{
    using boost::regex;
    using boost::regex_error;
    using boost::regex_replace;
    using boost::regex_search;
    using boost::regex_match;
    using boost::smatch;
    namespace regex_constants = boost::regex_constants;
}

Non discuterò se questa è una cattiva idea o meno. Sosterrò comunque che lo mantiene pulito per il mio progetto e allo stesso tempo lo rende specifico: vero, devo usare Boost, ma lo sto usando come alla fine lo avrà libstdc ++. Sì, iniziare il tuo progetto e iniziare con uno standard (...) all'inizio fa molto per aiutare la manutenzione, lo sviluppo e tutto ciò che è coinvolto nel progetto!

Giusto per chiarire qualcosa: in realtà non penso sia una buona idea usare un nome di classe / qualunque cosa nella STL deliberatamente e più specificamente al posto di. La stringa è l'eccezione (ignora la prima, sopra, o la seconda qui, gioco di parole se devi) per me dato che non mi piaceva l'idea di "String".

Così com'è, sono ancora molto orientato verso C e contro C ++. Dettagli risparmiati, gran parte di ciò su cui lavoro si adatta di più a C (ma è stato un buon esercizio e un buon modo per farmi a. Impara un'altra lingua e b. Cerca di non essere meno distorto rispetto a oggetto / classi / ecc. Che è forse meglio affermato come meno di mentalità chiusa, meno arrogante e più accettante.). Ma ciò che è utile è ciò che alcuni hanno già suggerito: uso davvero list (è abbastanza generico, no?), E ordina (stessa cosa) per nominarne due che causerebbero uno scontro di nomi se dovessi fare using namespace std;, e così a tal fine preferisco essere specifico, in controllo e sapendo che se intendo che sia l'uso standard, dovrò specificarlo. In parole povere: non dare per scontato.

E per rendere parte della regex di Boost std. Lo faccio per l'integrazione futura e - di nuovo, lo ammetto pienamente, questo è pregiudizio - Non penso che sia brutto come boost::regex:: .... In effetti, questa è un'altra cosa per me. Ci sono molte cose in C ++ che devo ancora arrivare ad accettare completamente in aspetti e metodi (un altro esempio: modelli variadici contro argomenti var [anche se ammetto che i modelli variadici sono molto utili!]). Anche quelli che accetto sono stati difficili e ho ancora problemi con loro.



7

Dalle mie esperienze, se hai più librerie che usano dire cout, ma per uno scopo diverso potresti usare quello sbagliato cout.

Per esempio, se digito, using namespace std;ed using namespace otherlib;e inserite solo cout(che risulta essere in entrambi), piuttosto che std::cout(o 'otherlib::cout'), si potrebbe utilizzare quello sbagliato, e ottenere gli errori. È molto più efficace ed efficiente da usare std::cout.


6

Con identificatori importati non qualificati hai bisogno di strumenti di ricerca esterni come grep per scoprire dove vengono dichiarati gli identificatori. Ciò rende più difficile il ragionamento sulla correttezza del programma.


6

Dipende da dove si trova. Se si tratta di un'intestazione comune, stai diminuendo il valore dello spazio dei nomi unendolo allo spazio dei nomi globale. Tieni presente che questo potrebbe essere un modo pulito per creare moduli globali.


6

Questa è una cattiva pratica, spesso nota come inquinamento dello spazio dei nomi globale. Possono verificarsi problemi quando più di uno spazio dei nomi ha lo stesso nome di funzione con firma, quindi sarà ambiguo per il compilatore decidere quale chiamare e tutto ciò può essere evitato quando si specifica lo spazio dei nomi con la chiamata della funzione come std::cout. Spero che sia di aiuto. :)


5

Per rispondere alla tua domanda la guardo in questo modo praticamente: molti programmatori (non tutti) invocano lo spazio dei nomi std. Quindi si dovrebbe avere l'abitudine di NON usare cose che ostacolano o usano gli stessi nomi di ciò che è nello spazio dei nomi std. Questo è molto concesso, ma non tanto rispetto al numero di possibili parole coerenti e pseudonimi che possono essere inventati in senso stretto.

Voglio dire davvero ... dire "non fare affidamento sul fatto che questo sia presente" ti sta solo impostando per fare affidamento sul fatto che NON sia presente. Avrai costantemente problemi a prendere in prestito frammenti di codice e a ripararli costantemente. Mantieni le tue cose definite dall'utente e prese in prestito in un ambito limitato come dovrebbero essere e risparmia MOLTO con i globi (onestamente i globi dovrebbero quasi sempre essere l'ultima risorsa ai fini di "compilare ora, sanità mentale più tardi"). Veramente penso che sia un cattivo consiglio da parte del tuo insegnante perché l'uso di std funzionerà sia per "cout" che per "std :: cout" ma NON usare std funzionerà solo per "std :: cout". Non sarai sempre abbastanza fortunato da scrivere tutto il tuo codice.

NOTA: non concentrarti troppo sui problemi di efficienza finché non impari un po 'su come funzionano i compilatori. Con una piccola esperienza di codifica non devi imparare molto su di loro prima di capire quanto sono in grado di generalizzare il buon codice in qualcosa di semplice. Semplicemente come se avessi scritto tutto in C. Il buon codice è tanto complesso quanto deve essere.


Dato che molte persone sembrano inconsapevoli delle utili funzioni standard delle biblioteche (reinventando le cose <algorithm>, per esempio), sembra un po 'eccessivo immaginare che le stesse persone possano evitare in modo affidabile quegli identificatori. Cerca nel tuo codice e dimmi che non hai mai chiamato una variabile o una funzione count. O distance, o log, destroy, launch, visit, beta, sample, messages, clamp, erase, copy, modulus, left, ecc non parlare di tutti gli identificatori non ancora in stdche rompere il vostro codice quando C ++ 35 viene fuori ...
Toby Speight
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.