Cosa sono gli spazi dei nomi in linea?

C ++ 11 consente lo inline namespace , tutti i membri sono anche automaticamente nello namespace allegato. Non riesco a pensare ad alcuna utile applicazione di questo – qualcuno può per favore dare un breve esempio succinto di una situazione in cui è necessario uno inline namespace e dove è la soluzione più idiomatica?

(Inoltre, non è chiaro per me cosa succede quando uno namespace è dichiarato in inline in una ma non tutte le dichiarazioni, che possono vivere in file diversi. Non è questo l’accattonaggio per problemi?)

Gli spazi dei nomi in linea sono una versione di libreria simile al controllo delle versioni dei simboli , ma implementata esclusivamente a livello di C ++ 11 (cioè multipiattaforma) invece di essere una caratteristica di uno specifico formato eseguibile binario (ad esempio specifico della piattaforma).

È un meccanismo attraverso il quale un autore di librerie può creare uno spazio dei nomi annidato e agire come se tutte le sue dichiarazioni fossero nello spazio dei nomi circostante (i namespace in linea possono essere annidati, quindi i nomi “più nidificati” percolano fino al primo non -inline namespace e guarda e agisce come se le loro dichiarazioni fossero in uno qualsiasi dei namespace in mezzo).

Ad esempio, considera l’implementazione STL del vector . Se disponessimo di spazi dei nomi in linea dall’inizio di C ++, in C ++ 98 l’intestazione potrebbe essere simile a questa:

 namespace std { #if __cplusplus < 1997L // pre-standard C++ inline #endif namespace pre_cxx_1997 { template  __vector_impl; // implementation class template  // eg w/o allocator argument class vector : __vector_impl { // private inheritance // ... }; } #if __cplusplus >= 1997L // C++98/03 or later // (ifdef'ed out b/c it probably uses new language // features that a pre-C++98 compiler would choke on) # if __cplusplus == 1997L // C++98/03 inline # endif namespace cxx_1997 { // std::vector now has an allocator argument template  > class vector : pre_cxx_1997::__vector_impl { // the old impl is still good // ... }; // and vector is special: template  > class vector { // ... }; }; #endif // C++98/03 or later } // namespace std 

A seconda del valore di __cplusplus , viene scelta l’una o l’altra implementazione vector . Se il codice base è stato scritto in pre-C ++ 98 volte e si scopre che la versione di vector C ++ 98 sta causando problemi quando si aggiorna il compilatore, “tutto” è necessario trovare i riferimenti a std::vector nel tuo codebase e sostituiscili con std::pre_cxx_1997::vector .

Arriva lo standard successivo, e il fornitore STL ripete nuovamente la procedura, introducendo un nuovo spazio dei nomi per std::vector con supporto emplace_back (che richiede C ++ 11) e __cplusplus == 201103L quello iff __cplusplus == 201103L .

OK, quindi perché ho bisogno di una nuova funzione di lingua per questo? Posso già fare quanto segue per avere lo stesso effetto, no?

 namespace std { namespace pre_cxx_1997 { // ... } #if __cplusplus < 1997L // pre-standard C++ using namespace pre_cxx_1997; #endif #if __cplusplus >= 1997L // C++98/03 or later // (ifdef'ed out b/c it probably uses new language // features that a pre-C++98 compiler would choke on) namespace cxx_1997 { // ... }; # if __cplusplus == 1997L // C++98/03 using namespace cxx_1997; # endif #endif // C++98/03 or later } // namespace std 

A seconda del valore di __cplusplus , ottengo l’una o l’altra delle implementazioni.

E saresti quasi corretto.

Si consideri il seguente codice utente C ++ 98 valido (è stato permesso di specializzare completamente i modelli che vivono nello spazio dei nomi std in C ++ 98):

 // I don't trust my STL vendor to do this optimisation, so force these // specializations myself: namespace std { template <> class vector : my_special_vector { // ... }; template <> class vector : my_special_vector { // ... }; // ...etc... } // namespace std 

Questo è un codice perfettamente valido in cui l’utente fornisce la propria implementazione di un vettore per un insieme di tipi in cui apparentemente conosce un’implementazione più efficiente di quella che si trova nella (sua copia di) STL.

Ma : quando si specializza un modello, è necessario farlo nello spazio dei nomi in cui è stato dichiarato. Lo standard dice che il vector è dichiarato nello spazio dei nomi std , quindi è lì che l’utente giustamente si aspetta di specializzare il tipo.

Questo codice funziona con uno spazio dei nomi non versionato std , o con la funzione di spazio dei nomi inline C ++ 11, ma non con il trucco delle versioni che utilizzava lo using namespace , perché espone i dettagli dell’implementazione che il vero spazio dei nomi in cui il vector era definito non è stato std direttamente.

Esistono altri fori in base ai quali è ansible rilevare lo spazio dei nomi annidato (vedere i commenti di seguito), ma i namespace in linea li collegano tutti. E questo è tutto ciò che c’è da fare. Immensamente utile per il futuro, ma AFAIK lo Standard non prescrive nomi di spazi nomi in linea per la propria libreria standard (mi piacerebbe essere smentito su questo, però), quindi può essere utilizzato solo per librerie di terze parti, non lo stesso standard (a meno che i produttori di compilatori non concordino su uno schema di denominazione).

http://www.stroustrup.com/C++11FAQ.html#inline-namespace (un documento scritto da e gestito da Bjarne Stroustrup, che dovresti sapere delle motivazioni per la maggior parte delle funzionalità di C ++ 11. )

In base a ciò, è necessario consentire il controllo delle versioni per compatibilità con le versioni precedenti. Si definiscono più spazi dei nomi interni e si rende in linea quello più recente. O comunque, quello predefinito per le persone a cui non interessa il controllo delle versioni. Suppongo che la più recente potrebbe essere una versione futura o all’avanguardia che non è ancora predefinita.

L’esempio dato è:

 // file V99.h: inline namespace V99 { void f(int); // does something better than the V98 version void f(double); // new feature // ... } // file V98.h: namespace V98 { void f(int); // does something // ... } // file Mine.h: namespace Mine { #include "V99.h" #include "V98.h" } #include "Mine.h" using namespace Mine; // ... V98::f(1); // old version V99::f(1); // new version f(1); // default version 

Non vedo immediatamente perché non si using namespace V99; nel Mine spazio dei nomi Mine , ma non devo capire del tutto il caso d’uso per prendere la parola di Bjarne sulla motivazione del comitato.