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


242

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

// 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 di progettazione che potrebbero indurre a utilizzare uno spazio dei nomi senza nome? quali sono i vantaggi e gli svantaggi?

Risposte:


189

Gli spazi dei nomi senza nome sono un'utilità per rendere locale un'unità di traduzione identificatore. Si comportano come se si scegliesse 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 aggiuntivo che utilizza il corpo vuoto è importante, quindi puoi già fare riferimento all'interno del corpo dello spazio dei nomi a identificatori come ::namequelli definiti in quello spazio dei nomi, poiché la direttiva che utilizza è già avvenuta.

Ciò significa che puoi avere funzioni gratuite chiamate (ad esempio) helpche possono esistere in più unità di traduzione e che non si scontreranno al momento del collegamento. L'effetto è quasi identico all'utilizzo della staticparola chiave utilizzata in C che è possibile inserire nella dichiarazione degli identificatori. Gli spazi dei nomi senza nome sono un'alternativa superiore, essendo anche in grado di rendere locale un'unità di traduzione del tipo.

namespace { int a1; }
static int a2;

Entrambi asono unità di traduzione locali e non si scontreranno al momento del collegamento. Ma la differenza è che a1nello spazio dei nomi anonimo ottiene un nome univoco.

Leggi l'articolo eccellente su comeau-computing Perché viene utilizzato uno spazio dei nomi senza nome anziché statico? ( Mirror di Archive.org ).


Spieghi la relazione static. Puoi anche confrontare con __attribute__ ((visibility ("hidden")))?
Phinz,

74

Avere qualcosa in uno spazio dei nomi anonimo significa che è locale in questa unità di traduzione (file .cpp e tutte le sue inclusioni) 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 globale statica o una funzione statica, ma può essere usato anche per le definizioni di classe (e dovrebbe essere usato piuttosto che staticin C ++).

Tutti gli spazi dei nomi anonimi nello stesso file sono trattati come lo stesso spazio dei nomi e tutti gli spazi dei nomi anonimi in file diversi sono distinti. Uno spazio dei nomi anonimo è l'equivalente di:

namespace __unique_compiler_generated_identifer0x42 {
    ...
}
using namespace __unique_compiler_generated_identifer0x42;

14

Lo spazio dei nomi senza nome limita l'accesso di classe, variabile, funzione e oggetti al file in cui è definito. La funzionalità dello spazio dei nomi senza nome è simile alla staticparola chiave in C / C ++.
staticla parola chiave limita l'accesso della variabile globale e la funzione al file in cui sono definiti.
C'è differenza tra lo spazio dei nomi senza nome e la staticparola chiave a causa del quale lo spazio dei nomi senza nome ha un vantaggio rispetto a statico. staticla parola chiave può essere utilizzata con variabile, funzione e oggetti ma non con classe 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 possibile con uno spazio dei nomi senza nome. Per esempio,

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

13

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 dello spazio dei nomi non richiedono alcun collegamento esterno, il compilatore è più libero di eseguire l'ottimizzazione aggressiva del codice all'interno dello spazio dei nomi. Ad esempio, una funzione chiamata più volte una volta in un ciclo può essere incorporata senza alcun impatto sulla dimensione del codice.

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

#include <iostream>

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

5
Troppo bello per essere vero - ho provato questo segmento su gcc 4-1-2, usando l'ottimizzazione O3, con e senza l'istruzione dello spazio dei nomi: -> Ottenuto allo stesso tempo (3sec, con -O3 e 4sec con -O3)
Theo,

2
Questo codice era intenzionalmente complesso nel tentativo di convincere il compilatore a non incorporare b e add_val in main. L'ottimizzazione O3 utilizza molte operazioni di allineamento, indipendentemente dal costo per gonfiare il codice. Ci sono comunque, probabilmente, funzioni in cui O3 non incorporerebbe add_val. Potresti provare a rendere add_val più complesso o chiamarlo più volte da main in diverse circostanze.
xioxox,

5
@Daniel: cosa mi sto perdendo? come letto, hai detto che rispetto -O3a se stesso, poi hai detto che 3 vs 4 secondi sono "la stessa ora". nessuno di questi ha un senso. ho il sospetto che la vera spiegazione sarebbe, ma che cos'è?
underscore_d

@underscore_d La risposta afferma che -O2 è stato usato in entrambi i casi, non -O3. Diversi livelli di ottimizzazione possono comportarsi diversamente. Inoltre, diverse versioni del compilatore possono comportarsi diversamente (la risposta può essere obsoleta, cioè)
Paul Stelian,

1
@PaulStelian Lo so, ma sembra abbastanza chiaro che stavo rispondendo non alla risposta di xioxox ma piuttosto al commento di Theo (anche se il suo nome è cambiato o mi sono confuso in qualche modo)
underscore_d

12

L'esempio mostra che le persone nel progetto a cui hai aderito non capiscono spazi dei nomi anonimi :)

namespace {
    const int SIZE_OF_ARRAY_X;
    const int SIZE_OF_ARRAY_Y;

Non è necessario che si trovino in uno spazio dei nomi anonimo, poiché l' constoggetto ha già un collegamento statico e quindi non può essere in conflitto con identificatori con lo stesso nome in un'altra unità di traduzione.

    bool getState(userType*,otherUserType*);
}

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

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

Qui. Sono caduto nella stessa trappola (ci sono parole nello standard che suggeriscono che la statica dei file è in qualche modo deprecata a favore di spazi dei nomi anonimi), ma lavorando in un grande progetto C ++ come KDE, hai un sacco di persone che girano la testa nel modo giusto di nuovo in giro :)


10
Poiché gli spazi dei nomi senza nome c ++ 11 hanno un collegamento interno (sezione 3.5 nello standard o en.cppreference.com/w/cpp/language/namespace#Unnamed_namespaces )
Emile Vrijdags,

11
"Questi non hanno bisogno di essere in uno spazio dei nomi anonimo" Tecnicamente, certo - ma comunque, non fa male metterli in uno, come promemoria visivo della loro semantica e renderlo (ancora più) banale per rimuovere la constness più tardi se lo si desidera. Dubito che ciò significhi che il team del PO "non capisce" nulla! Inoltre, il bit relativo alle funzioni negli spazi dei nomi anonimi con collegamento esterno è errato in C ++ 11 in poi, come indicato. A quanto ho capito, hanno risolto un problema di argomenti di modello che in precedenza necessitavano di un collegamento esterno, quindi potevano consentire spazi dei nomi senza nome (in grado di contenere argomenti modello) di avere un collegamento interno.
underscore_d

11

Uno spazio dei nomi anonimo rende le variabili, le funzioni, le classi e così via disponibili solo all'interno di quel file. Nel tuo esempio è un modo per evitare le variabili globali. Non vi è alcuna differenza nelle prestazioni di runtime o tempo di compilazione.

Non ci sono molti vantaggi o svantaggi oltre a "voglio che questa variabile, funzione, classe, ecc. Sia pubblica o privata?"


2
Ci possono essere differenze di prestazioni - vedi la mia risposta qui. Permette al compilatore di ottimizzare meglio il codice.
xioxox,

2
Tu hai un punto; almeno per quanto riguarda C ++ oggi. Tuttavia, in C ++ 98 / C ++ 03 le cose richieste hanno un collegamento esterno per poter essere utilizzate come argomenti modello. Dato che le cose negli spazi dei nomi anonimi sono disponibili come argomenti modello, avrebbero un collegamento esterno (almeno in pre-C ++ 11) anche se non ci fosse modo di fare riferimento a loro dall'esterno del file. Penso che ci possa essere stata una certa capacità di confonderlo, perché lo standard richiede solo che le cose si comportino come se le regole fossero applicate; ed è talvolta possibile farlo senza applicare veramente le regole.
Max Lybbert,
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.