Um die Hauptpunkte zusammenzufassen, waren using namespace v99
und inline namespace
nicht dasselbe, Ersteres war ein Workaround, um Bibliotheken zu versionieren, bevor ein dediziertes Schlüsselwort (inline) in C++11 eingeführt wurde, das die Probleme bei der Verwendung von using
behob und dabei dieselbe Versionierungsfunktionalität bereitstellte. Die Verwendung von using namespace
verursachte früher Probleme mit ADL (obwohl ADL jetzt using
-Direktiven zu folgen scheint) und die Auslagerung der Spezialisierung einer Bibliotheksklasse / -funktion usw. durch den Benutzer außerhalb des eigentlichen Namensraums funktionierte nicht, wenn dies außerhalb des tatsächlichen Namensraums erfolgte (dessen Name der Benutzer nicht wissen sollte und nicht sollte, d.h. der Benutzer müsste B::abi_v2:: anstatt nur B:: für die Spezialisierung verwenden, um sie aufzulösen).
//Bibliothekscode
namespace B { //Name der Bibliothek, den der Benutzer kennt
namespace A { //ABI-Version, von der der Benutzer nichts weiß
template class myclass{int a;};
}
using namespace A; //Früherer inline-Namespace-Versionstrick
}
// Benutzercode
namespace B { //Benutzer denkt, die Bibliothek verwendet diesen Namespace
template<> class myclass {};
}
Dies zeigt eine statische Analysewarnung first declaration of class template specialization of 'myclass' outside namespace 'A' is a C++11 extension [-Wc++11-extensions]
. Aber wenn Sie den Namespace A inline machen, löst der Compiler die Spezialisierung korrekt auf. Mit den C++11-Erweiterungen verschwindet das Problem jedoch.
Ausgelagerte Definitionen werden bei Verwendung von using
nicht aufgelöst; sie müssen in einem verschachtelten/nicht verschachtelten Erweiterungsnamensraumblock deklariert werden (das bedeutet, dass der Benutzer die ABI-Version erneut kennen muss, falls er aus irgendeinem Grund seine eigene Implementierung einer Funktion bereitstellen durfte).
#include
namespace A {
namespace B{
int a;
int func(int a);
template class myclass{int a;};
class C;
extern int d;
}
using namespace B;
}
int A::d = 3; //Kein Element namens 'd' im Namespace A
class A::C {int a;}; //Keine Klasse namens 'C' im Namespace 'A'
template<> class A::myclass {}; // funktioniert; Spezialisierung ist keine Auslagerung einer Deklaration
int A::func(int a){return a;}; //Auslagerung der Definition von 'func' stimmt mit keiner Deklaration im Namespace 'A' überein
namespace A { int func(int a){return a;};} //funktioniert
int main() {
A::a =1; // funktioniert; keine Auslagerung der Definition
}
Das Problem verschwindet, wenn man B inline macht.
Der andere Zweck von inline
-Namensräumen besteht darin, dem Bibliotheksautor ein transparentes Update der Bibliothek zu ermöglichen, 1) ohne den Benutzer zu zwingen, den Code mit dem neuen Namensraumnamen zu überarbeiten, und 2) einen Mangel an Verbose zu verhindern und 3) Abstraktion von API-unwichtigen Details bereitzustellen, während 4) die gleichen Vorteile bei der Verknüpfungsdiagnose und dem Verhalten bereitzustellen, die ein nicht-inline Namensraum bieten würde. Angenommen, Sie verwenden eine Bibliothek:
namespace library {
inline namespace abi_v1 {
class foo {
}
}
}
Es ermöglicht dem Benutzer, library::foo
aufzurufen, ohne die ABI-Version in der Dokumentation zu kennen oder einzuschließen, was sauberer aussieht. Das Verwenden von library::abiverison129389123::foo
würde unsauber aussehen.
Wenn ein Update für foo
vorgenommen wird, z.B. durch Hinzufügen eines neuen Elements zur Klasse, wird dies bestehende Programme auf API-Ebene nicht beeinflussen, da sie das Element nicht bereits verwenden, UND die Änderung am inline-Namespace-Namen wird auf API-Ebene nichts ändern, da library::foo
weiterhin funktionieren wird.
namespace library {
inline namespace abi_v2 {
class foo {
//neues Element
}
}
}
Jedoch für Programme, die damit verlinken, da der inline-Namensraumname in Symbolsymbole wie ein regulärer Namensraum eingebettet ist, wird die Änderung für den Linker nicht transparent sein. Wenn also die Anwendung nicht neu kompiliert wird, aber mit einer neuen Version der Bibliothek verlinkt ist, wird ein Fehler angezeigt, dass das Symbol abi_v1
nicht gefunden wurde, anstatt tatsächlich zu verlinken und dann aufgrund einer ABI-Inkompatibilität ein mysteriöser Logikfehler zur Laufzeit zu verursachen. Das Hinzufügen eines neuen Elements wird aufgrund der Änderung in der Typdefinition zu ABI-Kompatibilität führen, auch wenn dies das Programm nicht zur Compile-Zeit (API-Ebene) betrifft.
In diesem Szenario:
namespace library {
namespace abi_v1 {
class foo {
}
}
inline namespace abi_v2 {
class foo {
//neues Element
}
}
}
Wie bei der Verwendung von 2 nicht-inline Namensräumen ermöglicht es, eine neue Version der Bibliothek zu verlinken, ohne die Anwendung neu kompilieren zu müssen, weil abi_v1
in einem der globalen Symbole eingebettet wird und es die korrekte (alte) Typdefinition verwendet. Das erneute Kompilieren der Anwendung würde jedoch dazu führen, dass die Verweise zu library::abi_v2
aufgelöst werden.
Die Verwendung von using namespace
ist weniger funktional als die Verwendung von inline
(da ausgelagerte Definitionen nicht aufgelöst werden), bietet jedoch die gleichen 4 Vorteile wie oben beschrieben. Aber die eigentliche Frage ist, warum weiterhin ein Workaround verwendet wird, wenn es jetzt ein dediziertes Schlüsselwort gibt, um dies zu tun. Es ist eine bessere Praxis, weniger verbal (man muss nur 1 Zeile Code statt 2 ändern) und macht die Absicht klar.