Perché vengono utilizzati gli spazi dei nomi senza nome e quali sono i loro vantaggi?

Ho appena aderito a un nuovo progetto software C ++ e sto cercando di capire il design. Il progetto fa uso frequente degli spazi dei nomi senza nome. Ad esempio, qualcosa di simile potrebbe verificarsi in un file di definizione della class:

// newusertype.cc namespace { const int SIZE_OF_ARRAY_X; const int SIZE_OF_ARRAY_Y; bool getState(userType*,otherUserType*); } newusertype::newusertype(...) {... 

Quali sono le considerazioni sulla progettazione che potrebbero causare l’utilizzo di uno spazio dei nomi senza nome? quali sono i vantaggi e gli svantaggi?

(Di seguito, le cose scoperte sono cose che non si applicano più a C ++ 11, ma si applica a C ++ 03. C ++ 11 non fa quasi più differenze (se ci sono, sono solo lingua differenze di avvocato che non riesco a ricordare)).

Gli spazi dei nomi senza nome sono un’utilità per rendere un identificatore efficacemente l’ unità di traduzione locale. Si comportano come se dovessi scegliere un nome univoco per unità di traduzione per uno spazio dei nomi:

 namespace unique { /* empty */ } using namespace unique; namespace unique { /* namespace body. stuff in here */ } 

Il passaggio extra che usa il corpo vuoto è importante, quindi puoi già fare riferimento all’interno del corpo dello spazio dei nomi agli identificatori come ::name che sono definiti in quello spazio dei nomi, poiché la direttiva using aveva già luogo.

Ciò significa che puoi avere funzioni gratuite chiamate (ad esempio) help che possono esistere in più unità di traduzione e che non si scontreranno al momento del collegamento, poiché hanno tutti un nome univoco a causa del loro spazio dei nomi univoco in cui si trovano . L’effetto è quasi identico all’uso della parola chiave static usata in C che puoi inserire nella dichiarazione degli identificatori. static usato in questo modo è deprecato in C ++, dato che gli spazi dei nomi non nominati sono un’alternativa superiore, essendo in grado persino di rendere un tipo di unità di traduzione locale.

 namespace { int a1; } static int a2; 

Entrambi sono unità di traduzione locali e non si scontreranno al momento del collegamento. Ma la differenza è che l’ a1 nello spazio dei nomi anonimo ottiene solo un nome univoco. Ha ancora un collegamento esterno e può essere esportato nella tabella dei simboli del file object che si sta creando. Questo diventa importante se si desidera utilizzare il suo indirizzo come argomento del modello:

 template struct sample { }; // OK - a1 has external linkage sample<&a1> s1; // NOT OK - translation unit locality is done by giving a2 internal linkage. sample<&a2> s2; 

I parametri del modello devono avere un collegamento esterno, quindi in questo caso l’identificatore deve essere inserito in uno spazio dei nomi anonimo.

Leggi l’eccellente articolo su comeau-computing `Perché è usato un namespace senza nome invece di statico? .

Avere qualcosa in uno spazio dei nomi anonimo significa che è locale a questa unità di traduzione (file .cpp e tutti i suoi inclusi) questo significa che se un altro simbolo con lo stesso nome è definito altrove, non ci sarà una violazione della One Definition Rule (ODR).

Questo è lo stesso del modo C di avere una variabile statica globale o una funzione statica, ma può essere usato anche per le definizioni di class (e dovrebbe essere usato piuttosto che static in C ++).

Tutti gli spazi dei nomi anonimi nello stesso file vengono considerati come lo stesso spazio dei nomi e tutti gli spazi dei nomi anonimi in file diversi sono distinti. Un namespace anonimo è l’equivalente di:

 namespace __unique_compiler_generated_identifer0x42 { ... } using namespace __unique_compiler_generated_identifer0x42; 

L’esempio mostra che le persone nel progetto che hai aderito non comprendono gli spazi dei nomi anonimi 🙂

 namespace { const int SIZE_OF_ARRAY_X; const int SIZE_OF_ARRAY_Y; 

Questi non devono necessariamente trovarsi in uno spazio dei nomi anonimo, poiché l’object const già un collegamento statico e quindi non può potenzialmente entrare in conflitto con identificatori con lo stesso nome in un’altra unità di traduzione.

  bool getState(userType*,otherUserType*); } 

E questa è in realtà una getState() : getState() ha un collegamento esterno. Di solito è preferibile preferire il collegamento statico, poiché questo non inquina la tabella dei simboli. È meglio scrivere

 static bool getState(/*...*/); 

Qui. Sono caduto nella stessa trappola (c’è una formulazione nello standard che suggerisce che le statistiche dei file sono in qualche modo deprecate a favore degli spazi dei nomi anonimi), ma lavorando in un grande progetto C ++ come KDE, ottieni molte persone che girano la testa nel modo giusto di nuovo intorno 🙂

Oltre alle altre risposte a questa domanda, l’utilizzo di uno spazio dei nomi anonimo può anche migliorare le prestazioni. Poiché i simboli all’interno del namespace non hanno bisogno di alcun collegamento esterno, il compilatore è più libero per eseguire l’ottimizzazione aggressiva del codice all’interno del namespace. Ad esempio, una funzione chiamata più volte una volta in un ciclo può essere sottolineata senza alcun impatto sulle dimensioni del codice.

Ad esempio, sul mio sistema il seguente codice impiega circa il 70% del tempo di esecuzione se si utilizza lo spazio dei nomi anonimo (x86-64 gcc-4.6.3 e -O2, si noti che il codice aggiuntivo in add_val rende il compilatore non voluto includere due volte).

 #include  namespace { double a; void b(double x) { a -= x; } void add_val(double x) { a += x; if(x==0.01) b(0); if(x==0.02) b(0.6); if(x==0.03) b(-0.1); if(x==0.04) b(0.4); } } int main() { a = 0; for(int i=0; i<1000000000; ++i) { add_val(i*1e-10); } std::cout << a << '\n'; return 0; } 

Un namespace anonimo rende disponibili le variabili, le funzioni, le classi, ecc. Incluse solo all’interno di quel file. Nel tuo esempio è un modo per evitare variabili globali. Non c’è nessuna differenza di prestazioni in termini di tempo di esecuzione o compilazione.

Non c’è un vantaggio o uno svantaggio oltre a “voglio che questa variabile, funzione, class, ecc. Sia pubblica o privata?”

Lo spazio dei nomi senza nome limita l’accesso di class, variabile, funzione e oggetti al file in cui è definito. La funzionalità dello spazio dei nomi senza nome è simile alla parola chiave static in C / C ++.
static parola chiave static limita l’accesso della variabile globale e la funzione al file in cui sono definiti.
Esiste una differenza tra lo spazio dei nomi senza nome e static parola chiave static causa della quale lo spazio dei nomi senza nome ha un vantaggio rispetto allo statico. static parola chiave static può essere utilizzata con la variabile, la funzione e gli oggetti ma non con la class definita dall’utente.
Per esempio:

 static int x; // Correct 

Ma,

 static class xyz {/*Body of class*/} //Wrong static structure {/*Body of structure*/} //Wrong 

Ma lo stesso può essere ansible con namespace senza nome. Per esempio,

  namespace { class xyz {/*Body of class*/} static structure {/*Body of structure*/} } //Correct