C++
Namensräume
Suche…
Einführung
Ein Namensraum ist ein deklaratives Präfix für Funktionen, Klassen, Typen usw., um Namenskollisionen bei der Verwendung mehrerer Bibliotheken zu verhindern.
Syntax
- Namespace - Kennung (opt) {declaration-Seq}
- Inline-Namespace- ID ( opt ) { Deklaration-seq } / * seit C ++ 11 * /
- Inline ( opt ) -Namensraum Attribut- Bezeichner -Seq- Bezeichner ( Opt ) { Deklaration-Seq } / * seit C ++ 17 * /
- Namespace einschließender-Namespace- Bezeichner : ID { Deklaration-Seq } / * seit C ++ 17 * /
- Namespace- ID = Qualifizierter-Namespace- Bezeichner ;
- unter Verwendung des Namespaces verschachtelter-Name-Bezeichner ( opt ) Namespace-Name ;
- Attributspezifizierer-seq unter Verwendung des Namespaces verschachtelter Namensspezifizierer ( opt ) Namespace-Name ; / * seit C ++ 11 * /
Bemerkungen
Das Schlüsselwort namespace
hat drei verschiedene Bedeutungen je nach Kontext:
Wenn auf einen optionalen Namen und eine geschweifte Klammerfolge von Deklarationen gefolgt wird, wird ein neuer Namespace definiert oder ein vorhandener Namespace um diese Deklarationen erweitert. Wenn der Name weggelassen wird, ist der Namespace ein unbenannter Namespace .
Wenn ein Name und ein Gleichheitszeichen gefolgt werden, wird ein Namespace-Alias angegeben .
Wenn vorangestelltem
using
und von einem Namespace - Namen gefolgt, bildet es ein using - Direktive , die Namen im angegebenen Namespace ermöglicht durch unqualifizierte Namen - Suche gefunden werden (aber nicht die Namen im aktuellen Bereich neu deklarieren). Eine using-Direktive darf nicht im Klassenbereich vorkommen.
using namespace std;
ist entmutigt. Warum? Denn der namespace std
ist riesig! Dies bedeutet, dass die Wahrscheinlichkeit groß ist, dass Namen kollidieren:
//Really bad!
using namespace std;
//Calculates p^e and outputs it to std::cout
void pow(double p, double e) { /*...*/ }
//Calls pow
pow(5.0, 2.0); //Error! There is already a pow function in namespace std with the same signature,
//so the call is ambiguous
Was sind Namespaces?
Ein C ++ - Namespace ist eine Sammlung von C ++ - Entitäten (Funktionen, Klassen, Variablen), deren Namen den Namen des Namespaces voranstellen. Beim Schreiben von Code innerhalb eines Namespaces müssen benannten Entitäten, die zu diesem Namespace gehören, nicht der Name des Namespaces vorangestellt werden. Entitäten außerhalb dieses Namespaces müssen jedoch den vollständig qualifizierten Namen verwenden. Der vollständig qualifizierte Name hat das Format <namespace>::<entity>
. Beispiel:
namespace Example
{
const int test = 5;
const int test2 = test + 12; //Works within `Example` namespace
}
const int test3 = test + 3; //Fails; `test` not found outside of namespace.
const int test3 = Example::test + 3; //Works; fully qualified name used.
Namespaces sind nützlich, um zusammengehörige Definitionen zu gruppieren. Nehmen Sie die Analogie eines Einkaufszentrums. In der Regel ist ein Einkaufszentrum in mehrere Geschäfte aufgeteilt, von denen jeder Artikel aus einer bestimmten Kategorie verkauft. Ein Geschäft kann Elektronik verkaufen, während ein anderes Geschäft Schuhe verkaufen kann. Diese logischen Trennungen in den Geschäftstypen helfen den Käufern, die Artikel zu finden, nach denen sie suchen. Namespaces helfen C ++ - Programmierern wie Käufern dabei, die gewünschten Funktionen, Klassen und Variablen zu finden, indem sie auf logische Weise organisiert werden. Beispiel:
namespace Electronics
{
int TotalStock;
class Headphones
{
// Description of a Headphone (color, brand, model number, etc.)
};
class Television
{
// Description of a Television (color, brand, model number, etc.)
};
}
namespace Shoes
{
int TotalStock;
class Sandal
{
// Description of a Sandal (color, brand, model number, etc.)
};
class Slipper
{
// Description of a Slipper (color, brand, model number, etc.)
};
}
Es ist ein einzelner Namespace vordefiniert. Hierbei handelt es sich um den globalen Namespace, der keinen Namen hat, aber mit ::
bezeichnet werden kann. Beispiel:
void bar() {
// defined in global namespace
}
namespace foo {
void bar() {
// defined in namespace foo
}
void barbar() {
bar(); // calls foo::bar()
::bar(); // calls bar() defined in global namespace
}
}
Namensräume erstellen
Das Erstellen eines Namespaces ist sehr einfach:
//Creates namespace foo
namespace Foo
{
//Declares function bar in namespace foo
void bar() {}
}
So rufen Sie bar
, müssen Sie zuerst den Namespace angeben, gefolgt von dem Bereichsauflösungsoperator ::
:
Foo::bar();
Es ist erlaubt, einen Namespace in einem anderen zu erstellen, zum Beispiel:
namespace A
{
namespace B
{
namespace C
{
void bar() {}
}
}
}
Der obige Code könnte folgendermaßen vereinfacht werden:
namespace A::B::C
{
void bar() {}
}
Namensräume erweitern
Eine nützliche Funktion von namespace
ist, dass Sie sie erweitern (Mitglieder hinzufügen) können.
namespace Foo
{
void bar() {}
}
//some other stuff
namespace Foo
{
void bar2() {}
}
Direktive verwenden
Das Schlüsselwort "using" hat drei Varianten. Zusammen mit dem Schlüsselwort 'Namespace' schreiben Sie eine 'using-Direktive':
Wenn Sie nicht Foo::
vor jedem Zeugs im Namespace Foo
schreiben möchten, können Sie den using namespace Foo;
alles aus Foo
zu importieren.
namespace Foo
{
void bar() {}
void baz() {}
}
//Have to use Foo::bar()
Foo::bar();
//Import Foo
using namespace Foo;
bar(); //OK
baz(); //OK
Es ist auch möglich, ausgewählte Entitäten in einem Namensraum und nicht im gesamten Namensraum zu importieren:
using Foo::bar;
bar(); //OK, was specifically imported
baz(); // Not OK, was not imported
Ein Wort der Vorsicht: Die using namespace
in Header-Dateien wird in den meisten Fällen als fehlerhaft angesehen. In diesem Fall wird der Namespace in jede Datei importiert, die den Header enthält. Da gibt es keine Möglichkeit der „un- using
“ einem Namespace, dies zu Namespace Verschmutzung führen kann (mehr oder unerwarteter Symbole im globalen Namespace) oder, schlimmer noch, Konflikte. In diesem Beispiel wird das Problem veranschaulicht:
/***** foo.h *****/
namespace Foo
{
class C;
}
/***** bar.h *****/
namespace Bar
{
class C;
}
/***** baz.h *****/
#include "foo.h"
using namespace Foo;
/***** main.cpp *****/
#include "bar.h"
#include "baz.h"
using namespace Bar;
C c; // error: Ambiguity between Bar::C and Foo::C
Eine using-Direktive darf nicht im Klassenbereich vorkommen.
Argumentabhängige Suche
Beim Aufrufen einer Funktion ohne expliziten Namespace-Qualifikationsmerkmal kann der Compiler eine Funktion innerhalb eines Namespaces aufrufen, wenn sich einer der Parametertypen für diese Funktion ebenfalls in diesem Namespace befindet. Dies wird als "Argumentabhängige Suche" oder ADL bezeichnet:
namespace Test
{
int call(int i);
class SomeClass {...};
int call_too(const SomeClass &data);
}
call(5); //Fails. Not a qualified function name.
Test::SomeClass data;
call_too(data); //Succeeds
call
schlägt fehl, da keiner seiner Parametertypen aus dem Test
Namespace stammt. call_too
funktioniert, weil SomeClass
Mitglied von Test
und sich daher für ADL-Regeln qualifiziert.
Wann tritt ADL nicht auf
ADL tritt nicht auf, wenn die normale unqualifizierte Suche nach einem Klassenmitglied, einer im Blockbereich deklarierten Funktion oder einem nicht funktionellen Typ sucht. Zum Beispiel:
void foo();
namespace N {
struct X {};
void foo(X ) { std::cout << '1'; }
void qux(X ) { std::cout << '2'; }
}
struct C {
void foo() {}
void bar() {
foo(N::X{}); // error: ADL is disabled and C::foo() takes no arguments
}
};
void bar() {
extern void foo(); // redeclares ::foo
foo(N::X{}); // error: ADL is disabled and ::foo() doesn't take any arguments
}
int qux;
void baz() {
qux(N::X{}); // error: variable declaration disables ADL for "qux"
}
Inline-Namespace
inline namespace
enthält den Inhalt des Inline-Namespaces im umschließenden Namespace, so
namespace Outer
{
inline namespace Inner
{
void foo();
}
}
ist meistens äquivalent zu
namespace Outer
{
namespace Inner
{
void foo();
}
using Inner::foo;
}
Elemente aus Outer::Inner::
und denen, die Outer::
sind, sind jedoch identisch.
Folgendes ist also gleichwertig
Outer::foo();
Outer::Inner::foo();
Die Alternative using namespace Inner;
wäre für einige knifflige Teile als Schablonenspezialisierung nicht gleichwertig:
Zum
#include <outer.h> // See below
class MyCustomType;
namespace Outer
{
template <>
void foo<MyCustomType>() { std::cout << "Specialization"; }
}
Der Inline-Namespace ermöglicht die Spezialisierung von
Outer::foo
// outer.h // include guard omitted for simplification namespace Outer { inline namespace Inner { template <typename T> void foo() { std::cout << "Generic"; } } }
Während der
using namespace
die Spezialisierung vonOuter::foo
nicht zulässt// outer.h // include guard omitted for simplification namespace Outer { namespace Inner { template <typename T> void foo() { std::cout << "Generic"; } } using namespace Inner; // Specialization of `Outer::foo` is not possible // it should be `Outer::Inner::foo`. }
Inline-Namespace ist eine Möglichkeit, mehreren Versionen das Zusammenleben und die Verwendung der inline
Version zu ermöglichen
namespace MyNamespace
{
// Inline the last version
inline namespace Version2
{
void foo(); // New version
void bar();
}
namespace Version1 // The old one
{
void foo();
}
}
Und mit der Nutzung
MyNamespace::Version1::foo(); // old version
MyNamespace::Version2::foo(); // new version
MyNamespace::foo(); // default version : MyNamespace::Version1::foo();
Unbenannte / anonyme Namespaces
Ein unbenannter Namespace kann verwendet werden, um sicherzustellen, dass Namen über eine interne Verknüpfung verfügen (auf die nur die aktuelle Übersetzungseinheit verweist). Ein solcher Namespace wird wie jeder andere Namespace definiert, jedoch ohne den Namen:
namespace {
int foo = 42;
}
foo
ist nur in der Übersetzungseinheit sichtbar, in der es angezeigt wird.
Es wird empfohlen, niemals unbenannte Namespaces in Header-Dateien zu verwenden, da hierdurch für jede Übersetzungseinheit, in der sie enthalten ist, eine Version des Inhalts angezeigt wird. Dies ist besonders wichtig, wenn Sie Nicht-Konstante-Globals definieren.
// foo.h
namespace {
std::string globalString;
}
// 1.cpp
#include "foo.h" //< Generates unnamed_namespace{1.cpp}::globalString ...
globalString = "Initialize";
// 2.cpp
#include "foo.h" //< Generates unnamed_namespace{2.cpp}::globalString ...
std::cout << globalString; //< Will always print the empty string
Kompakte verschachtelte Namespaces
namespace a {
namespace b {
template<class T>
struct qualifies : std::false_type {};
}
}
namespace other {
struct bob {};
}
namespace a::b {
template<>
struct qualifies<::other::bob> : std::true_type {};
}
Sie können sowohl die Eingabe a
und b
Namespaces in einem Schritt mit namespace a::b
beginnend in C ++ 17.
Aliasing eines langen Namespaces
Dies wird normalerweise zum Umbenennen oder Verkürzen langer Namespace-Referenzen verwendet, z.
namespace boost
{
namespace multiprecision
{
class Number ...
}
}
namespace Name1 = boost::multiprecision;
// Both Type declarations are equivalent
boost::multiprecision::Number X // Writing the full namespace path, longer
Name1::Number Y // using the name alias, shorter
Alias-Deklarationsumfang
Alias Erklärung werden durch Verwendung der vorstehenden Aussagen betroffen
namespace boost
{
namespace multiprecision
{
class Number ...
}
}
using namespace boost;
// Both Namespace are equivalent
namespace Name1 = boost::multiprecision;
namespace Name2 = multiprecision;
Es ist jedoch einfacher zu verwechseln, welchen Namespace Sie als Aliasing verwenden, wenn Sie Folgendes haben:
namespace boost
{
namespace multiprecision
{
class Number ...
}
}
namespace numeric
{
namespace multiprecision
{
class Number ...
}
}
using namespace numeric;
using namespace boost;
// Not recommended as
// its not explicitly clear whether Name1 refers to
// numeric::multiprecision or boost::multiprecision
namespace Name1 = multiprecision;
// For clarity, its recommended to use absolute paths
// instead
namespace Name2 = numeric::multiprecision;
namespace Name3 = boost::multiprecision;
Namespace-Alias
Einem Namespace kann ein Alias ( dh ein anderer Name für denselben Namespace) mithilfe der namespace
ID bezeichner =
zugewiesen werden. Auf Mitglieder des Alias-Namespaces kann zugegriffen werden, indem sie mit dem Namen des Alias qualifiziert werden. Im folgenden Beispiel ist der geschachtelte Namespace AReallyLongName::AnotherReallyLongName
ungünstig, sodass die Funktion qux
lokal einen Alias N
deklariert. Auf Mitglieder dieses Namensraums kann dann einfach mit N::
zugegriffen werden.
namespace AReallyLongName {
namespace AnotherReallyLongName {
int foo();
int bar();
void baz(int x, int y);
}
}
void qux() {
namespace N = AReallyLongName::AnotherReallyLongName;
N::baz(N::foo(), N::bar());
}