Namespaces
Namespaces bieten eine Methode zur Vermeidung von Namenskonflikten in großen Projekten.
In einem Namespace-Block deklarierte Entitäten werden in einen Namespace-Bereich platziert, was verhindert, dass sie mit gleichnamigen Entitäten in anderen Bereichen verwechselt werden.
Außerhalb aller Namespace-Blöcke deklarierte Entitäten gehören zum
globalen Namespace
. Der globale Namespace gehört zum
globalen Scope
und kann explizit mit einem vorangestellten
::
referenziert werden. Obwohl er keine Deklaration besitzt, ist der globale Namespace kein
unbenannter Namespace
.
Mehrere Namespace-Blöcke mit demselben Namen sind erlaubt. Alle Deklarationen innerhalb dieser Blöcke werden im selben Namespace-Bereich deklariert.
Inhaltsverzeichnis |
Syntax
namespace
ns-name
{
Deklarationen
}
|
(1) | ||||||||
inline
namespace
ns-name
{
Deklarationen
}
|
(2) | (seit C++11) | |||||||
namespace
{
Deklarationen
}
|
(3) | ||||||||
ns-name
::
member-name
|
(4) | ||||||||
using
namespace
ns-name
;
|
(5) | ||||||||
using
ns-name
::
member-name
;
|
(6) | ||||||||
namespace
name
=
qualified-namespace
;
|
(7) | ||||||||
namespace
ns-name
::
member-name
{
Deklarationen
}
|
(8) | (seit C++17) | |||||||
namespace
ns-name
::
inline
member-name
{
Deklarationen
}
|
(9) | (seit C++20) | |||||||
Erklärung
Namespaces
inline
(optional)
namespace
attr
(optional)
identifier
{
namespace-body
}
|
|||||||||
inline
|
- |
(seit C++11)
falls vorhanden, macht dies zu einem Inline-Namespace (siehe unten). Darf nicht in der
extension-namespace-definition
erscheinen, wenn die
original-namespace-definition
nicht
inline
verwendete
|
||
| attr | - | (seit C++17) optionale Folge beliebig vieler Attribute | ||
| identifier | - |
entweder
|
||
| namespace-body | - | möglicherweise leere Folge von Deklarationen beliebiger Art (einschließlich Klassen- und Funktionsdefinitionen sowie verschachtelter Namespaces) |
Namespacedefinitionen sind nur auf Namespace-Ebene zulässig, einschließlich des globalen Gültigkeitsbereichs.
Um einen bestehenden Namespace wieder zu öffnen (formal, um eine extension-namespace-definition zu sein), muss die Suche nach dem identifier , der in der Namespace-Definition verwendet wird, zu einem Namespace-Namen (keinem Namespace-Alias) aufgelöst werden, der als Mitglied des umschließenden Namespace oder eines inline-Namespace innerhalb eines umschließenden Namespace deklariert wurde.
Der namespace-body definiert einen namespace scope , welcher sich auf name lookup auswirkt.
Alle Namen, die durch die Deklarationen eingeführt werden, die innerhalb des namespace-body erscheinen (einschließlich verschachtelter Namespace-Definitionen), werden zu Mitgliedern des Namespace identifier , unabhängig davon, ob es sich bei dieser Namespace-Definition um die ursprüngliche Namespace-Definition handelt (die den identifier eingeführt hat) oder um eine erweiternde Namespace-Definition (die den bereits definierten Namespace "wieder geöffnet" hat).
Ein Namespace-Mitglied, das innerhalb eines Namespace-Körpers deklariert wurde, kann außerhalb davon unter Verwendung expliziter Qualifikation definiert oder erneut deklariert werden
namespace Q { namespace V // V ist ein Mitglied von Q und ist vollständig innerhalb von Q definiert { // namespace Q::V { // C++17 Alternative zu den obigen Zeilen class C { void m(); }; // C ist ein Mitglied von V und ist vollständig innerhalb von V definiert // C::m ist nur deklariert void f(); // f ist ein Mitglied von V, aber hier nur deklariert } void V::f() // Definition des Mitglieds f von V außerhalb von V // Die einschließenden Namensräume von f sind weiterhin der globale Namensraum, Q und Q::V { extern void h(); // Dies deklariert ::Q::V::h } void V::C::m() // Definition von V::C::m außerhalb des Namensraums (und des Klassenkörpers) // Einschließende Namensräume sind der globale Namensraum, Q und Q::V {} }
Definitionen und Neudeklarationen außerhalb des Namespace sind nur erlaubt
- nach dem Punkt der Deklaration,
- im Namensbereichs-Scope, und
- in Namensbereichen, die den ursprünglichen Namensbereich umschließen (einschließlich des globalen Namensbereichs).
Außerdem müssen sie die qualified-id-Syntax verwenden.
namespace Q { namespace V // ursprüngliche Namensraumdefinition für V { void f(); // Deklaration von Q::V::f } void V::f() {} // OK void V::g() {} // Fehler: g() ist noch kein Mitglied von V namespace V // erweiterte Namensraumdefinition für V { void g(); // Deklaration von Q::V::g } } namespace R // kein umschließender Namensraum für Q { void Q::V::g() {} // Fehler: Q::V::g kann nicht innerhalb von R definiert werden } void Q::V::g() {} // OK: globaler Namensraum umschließt Q
Durch friend -Deklarationen in einer nicht-lokalen Klasse X eingeführte Namen werden zu Mitgliedern des innersten umschließenden Namespace von X, sie werden jedoch nicht durch gewöhnliche Namenssuche sichtbar (weder unqualifiziert noch qualifiziert ), es sei denn, eine entsprechende Deklaration wird im Namespace-Bereich bereitgestellt, entweder vor oder nach der Klassendefinition. Solche Namen können durch ADL gefunden werden, die sowohl Namespaces als auch Klassen berücksichtigt.
Nur der innerste umschließende Namespace wird bei einer solchen Friend-Deklaration berücksichtigt, wenn entschieden wird, ob der Name mit einem zuvor deklarierten Namen in Konflikt stehen würde.
void h(int); namespace A { class X { friend void f(X); // A::f ist ein Friend class Y { friend void g(); // A::g ist ein Friend friend void h(int); // A::h ist ein Friend, kein Konflikt mit ::h }; }; // A::f, A::g und A::h sind im Namensbereich nicht sichtbar // obwohl sie Mitglieder des Namensbereichs A sind X x; void g() // Definition von A::g { f(x); // A::X::f wird durch ADL gefunden } void f(X) {} // Definition von A::f void h(int) {} // Definition von A::h // A::f, A::g und A::h sind jetzt im Namensbereich sichtbar // und sie sind auch Friends von A::X und A::X::Y }
Inline-Namespaces
Ein Inline-Namespace ist ein Namespace, der das optionale Schlüsselwort
Member eines Inline-Namespaces werden in vielen Situationen (unten aufgelistet) so behandelt, als wären sie Member des umschließenden Namespaces. Diese Eigenschaft ist transitiv: Wenn ein Namespace N einen Inline-Namespace M enthält, der wiederum einen Inline-Namespace O enthält, dann können die Member von O so verwendet werden, als wären sie Member von M oder N.
// in C++14, std::literals and its member namespaces are inline { using namespace std::string_literals; // makes visible operator""s // from std::literals::string_literals auto str = "abc"s; } { using namespace std::literals; // makes visible both // std::literals::string_literals::operator""s // and std::literals::chrono_literals::operator""s auto str = "abc"s; auto min = 60s; } { using std::operator""s; // makes both std::literals::string_literals::operator""s // and std::literals::chrono_literals::operator""s visible auto str = "abc"s; auto min = 60s; } Hinweis: Die Regel über Spezialisierungen erlaubt Bibliotheksversionierung: Unterschiedliche Implementierungen einer Bibliotheksvorlage können in verschiedenen Inline-Namespaces definiert werden, während der Benutzer weiterhin den übergeordneten Namespace mit einer expliziten Spezialisierung der primären Vorlage erweitern kann:
Diesen Code ausführen
namespace Lib { inline namespace Lib_1 { template<typename T> class A; } template<typename T> void g(T) { /* ... */ } } /* ... */ struct MyClass { /* ... */ }; namespace Lib { template<> class A<MyClass> { /* ... */ }; } int main() { Lib::A<MyClass> a; g(a); // ok, Lib is an associated namespace of A } |
(seit C++11) |
Unbenannte Namensräume
Die unnamed-namespace-definition ist eine Namespace-Definition der Form
inline
(optional)
namespace
attr
(optional)
{
namespace-body
}
|
|||||||||
inline
|
- | (since C++11) falls vorhanden, macht dies zu einem Inline-Namespace |
| attr | - | (since C++17) optionale Sequenz beliebig vieler Attribute |
Diese Definition wird behandelt als eine Definition eines Namensraums mit eindeutigem Namen und einer using-Direktive im aktuellen Gültigkeitsbereich, die diesen unbenannten Namensraum nominiert (Hinweis: implizit hinzugefügte using-Direktive macht den Namensraum verfügbar für die qualifizierte Namenssuche und unqualifizierte Namenssuche , aber nicht für die argumentabhängige Suche ). Der eindeutige Name ist über das gesamte Programm hinweg eindeutig, aber innerhalb einer Übersetzungseinheit ordnet sich jede unbenannte Namensraumdefinition demselben eindeutigen Namen zu: Mehrere unbenannte Namensraumdefinitionen im selben Gültigkeitsbereich bezeichnen denselben unbenannten Namensraum.
namespace { int i; // definiert ::(unique)::i } void f() { i++; // inkrementiert ::(unique)::i } namespace A { namespace { int i; // A::(unique)::i int j; // A::(unique)::j } void g() { i++; } // A::(unique)::i++ } using namespace A; // führt alle Namen aus A in den globalen Namespace ein void h() { i++; // Fehler: ::(unique)::i und ::A::(unique)::i sind beide im Scope A::i++; // ok, inkrementiert ::A::(unique)::i j++; // ok, inkrementiert ::A::(unique)::j }
|
Obwohl Namen in einem unbenannten Namespace mit externer Verknüpfung deklariert werden können, sind sie von anderen Übersetzungseinheiten nie zugänglich, da ihr Namespace-Name eindeutig ist. |
(until C++11) |
|
Unbenannte Namespaces sowie alle direkt oder indirekt innerhalb eines unbenannten Namespaces deklarierten Namespaces besitzen interne Verknüpfung , was bedeutet, dass jeder Name, der innerhalb eines unbenannten Namespaces deklariert wird, interne Verknüpfung besitzt. |
(since C++11) |
Using-Deklarationen
Führt einen Namen, der an anderer Stelle definiert ist, in den deklarativen Bereich ein, in dem diese using-Deklaration erscheint.
using
typename
(optional)
nested-name-specifier
unqualified-id
;
|
(bis C++17) | ||||||||
using
declarator-list
;
|
(seit C++17) | ||||||||
typename
|
- |
das Schlüsselwort
typename
kann nach Bedarf verwendet werden, um
abhängige Namen
aufzulösen, wenn die using-Deklaration einen Member-Typ aus einer Basisklasse in eine Klassen-Template einführt
|
| nested-name-specifier | - |
eine Folge von Namen und Bereichsauflösungsoperatoren
::
, die mit einem Bereichsauflösungsoperator endet. Ein einzelnes
::
bezieht sich auf den globalen Namensraum.
|
| unqualified-id | - | ein id-Ausdruck |
| declarator-list | - |
kommagetrennte Liste von einem oder mehreren Deklaratoren der Form
typename
(optional)
nested-name-specifier
unqualified-id
. Ein Deklarator kann von einer Auslassungspunkten gefolgt werden, um eine
Paketentfaltung
anzuzeigen, obwohl diese Form nur in
abgeleiteten Klassendefinitionen
sinnvoll ist
|
Using-Deklarationen können verwendet werden, um Namespace-Mitglieder in andere Namespaces und Blockbereiche einzuführen, oder um Basisklassenmitglieder in abgeleitete Klassendefinitionen einzuführen , oder um Enumeratoren in Namespaces, Block- und Klassenbereiche einzuführen (since C++20) .
|
Eine using-Deklaration mit mehr als einem using-Deklarator entspricht einer entsprechenden Folge von using-Deklarationen mit jeweils einem using-Deklarator. |
(since C++17) |
Für die Verwendung in abgeleiteten Klassendefinitionen, siehe using declaration .
Durch eine using-Deklaration in einen Namensbereich eingeführte Namen können wie alle anderen Namen verwendet werden, einschließlich qualifizierter Suche aus anderen Geltungsbereichen:
void f(); namespace A { void g(); } namespace X { using ::f; // globales f ist nun sichtbar als ::X::f using A::g; // A::g ist nun sichtbar als ::X::g using A::g, A::g; // (C++17) OK: Doppeldeklaration im Namensbereich erlaubt } void h() { X::f(); // ruft ::f auf X::g(); // ruft A::g auf }
Wenn nach der Verwendung einer using-Deklaration, um ein Member aus einem Namespace zu übernehmen, der Namespace erweitert wird und zusätzliche Deklarationen für denselben Namen eingeführt werden, werden diese zusätzlichen Deklarationen nicht durch die using-Deklaration sichtbar (im Gegensatz zur using-Direktive). Eine Ausnahme besteht, wenn eine using-Deklaration ein Klassentemplate benennt: Partielle Spezialisierungen, die später eingeführt werden, sind effektiv sichtbar, weil ihr Lookup über das primäre Template erfolgt.
namespace A { void f(int); } using A::f; // ::f ist jetzt ein Synonym für A::f(int) namespace A // Namensraumerweiterung { void f(char); // ändert nicht, was ::f bedeutet } void foo() { f('a'); // ruft f(int) auf, obwohl f(char) existiert. } void bar() { using A::f; // dieses f ist ein Synonym für sowohl A::f(int) als auch A::f(char) f('a'); // ruft f(char) auf }
Using-Deklarationen können keine template-id oder einen Namensraum benennen , oder einen scoped enumerator (bis C++20) . Jeder Deklarator in einer Using-Deklaration führt einen und nur einen Namen ein, zum Beispiel führt eine Using-Deklaration für eine enumeration keine ihrer Enumeratoren ein.
Alle Einschränkungen für reguläre Deklarationen derselben Namen, Verdeckungs- und Überladungsregeln gelten für using-Deklarationen:
namespace A { int x; } namespace B { int i; struct g {}; struct x {}; void f(int); void f(double); void g(char); // OK: Funktionsname g verdeckt struct g } void func() { int i; using B::i; // Fehler: i wurde zweimal deklariert void f(char); using B::f; // OK: f(char), f(int), f(double) sind Überladungen f(3.5); // ruft B::f(double) auf using B::g; g('a'); // ruft B::g(char) auf struct g g1; // deklariert g1 mit Typ struct B::g using B::x; using A::x; // OK: verdeckt struct B::x x = 99; // weist A::x zu struct x x1; // deklariert x1 mit Typ struct B::x }
Wenn eine Funktion durch eine using-Deklaration eingeführt wurde, ist die Deklaration einer Funktion mit demselben Namen und Parameterliste fehlerhaft (es sei denn, die Deklaration ist für dieselbe Funktion). Wenn eine Funktionsvorlage durch eine using-Deklaration eingeführt wurde, ist die Deklaration einer Funktionsvorlage mit demselben Namen, Parametertypliste, Rückgabetyp und Template-Parameterliste fehlerhaft. Zwei using-Deklarationen können Funktionen mit demselben Namen und derselben Parameterliste einführen, aber wenn ein Aufruf dieser Funktion versucht wird, ist das Programm fehlerhaft.
namespace B { void f(int); void f(double); } namespace C { void f(int); void f(double); void f(char); } void h() { using B::f; // führt B::f(int), B::f(double) ein using C::f; // führt C::f(int), C::f(double) und C::f(char) ein f('h'); // ruft C::f(char) auf f(1); // Fehler: B::f(int) oder C::f(int)? void f(int); // Fehler: f(int) kollidiert mit C::f(int) und B::f(int) }
Wenn eine Entität deklariert, aber nicht in einem inneren Namespace definiert wird, und dann durch eine using-Deklaration im äußeren Namespace deklariert wird, und anschließend eine Definition im äußeren Namespace mit demselben unqualifizierten Namen erscheint, ist diese Definition ein Mitglied des äußeren Namespace und kollidiert mit der using-Deklaration:
namespace X { namespace M { void g(); // deklariert, aber definiert nicht X::M::g() } using M::g; void g(); // Fehler: Versuch X::g zu deklarieren, was mit X::M::g() kollidiert }
Allgemeiner gesagt, eine Deklaration, die in einem beliebigen Namensbereichs-Scope erscheint und einen Namen mit einem unqualifizierten Bezeichner einführt, führt immer ein Mitglied in den Namensbereich ein, in dem sie sich befindet, und nicht in einen anderen Namensbereich. Die Ausnahmen sind explizite Instanziierungen und explizite Spezialisierungen eines primären Templates, das in einem inline-Namensbereich definiert ist: Da sie keinen neuen Namen einführen, können sie unqualified-id in einem umschließenden Namensbereich verwenden.
Using-Direktiven
Eine using-directive ist eine block-declaration mit folgender Syntax:
attr
(optional)
using
namespace
nested-name-specifier
(optional)
namespace-name
;
|
(1) | ||||||||
| attr | - | (since C++11) beliebige Anzahl von Attributen , die auf diese using-Direktive anwendbar sind |
| nested-name-specifier | - |
eine Folge von Namen und Bereichsauflösungsoperatoren
::
, die mit einem Bereichsauflösungsoperator endet. Ein einzelnes
::
bezieht sich auf den globalen Namensraum. Bei der Suche nach den Namen in dieser Folge berücksichtigt
lookup
nur Namespace-Deklarationen
|
| namespace-name | - | ein Name eines Namespace. Bei der Suche nach diesem Namen berücksichtigt lookup nur Namespace-Deklarationen |
Using-Directives sind nur im Namespace- Scope und im Block-Scope erlaubt. Vom Standpunkt des unqualified name lookup aus betrachtet, ist für jeden Namen nach einer Using-Directive und bis zum Ende des Scopes, in dem sie erscheint, jeder Name aus dem namespace-name sichtbar, als ob er im nächstgelegenen einschließenden Namespace deklariert wäre, der sowohl die Using-Directive als auch den namespace-name enthält.
Die Using-Direktive fügt dem deklarativen Bereich, in dem sie erscheint, keine Namen hinzu (im Gegensatz zur Using-Deklaration) und verhindert daher nicht, dass identische Namen deklariert werden.
Using-Direktiven sind transitiv für die Zwecke der unqualified lookup : Wenn ein Gültigkeitsbereich eine Using-Direktive enthält, die einen namespace-name benennt, der selbst Using-Direktiven für einen anderen namespace-name-2 enthält, ist der Effekt derselbe, als ob die Using-Direktiven aus dem zweiten Namespace im ersten erscheinen. Die Reihenfolge, in der diese transitiven Namespaces auftreten, beeinflusst die Namenssuche nicht.
namespace A { int i; } namespace B { int i; int j; namespace C { namespace D { using namespace A; // Namen aus A werden in D "injiziert". // Unqualifizierte Namenssuche in D betrachtet diese Namen als im gleichen // Gültigkeitsbereich wie der globale Bereich (z.B. für Namensverdeckung). // Qualifizierte Suche mit Bezug auf D (D::name für einen Namen) // wird denselben Namen finden wie unqualifizierte Suche in D. int j; int k; int a = i; // i ist B::i, weil A::i durch B::i verdeckt wird int b = ::i; // Fehler: Es gibt immer noch kein i im globalen Namensraum } using namespace D; // Namen aus D und A werden in C injiziert int k = 89; // OK, einen Namen zu deklarieren, der identisch mit einem durch using eingeführten ist int l = k; // Mehrdeutig: C::k oder D::k int m = i; // ok: B::i verdeckt A::i int n = j; // ok: D::j verdeckt B::j } } // Dies sind alle äquivalente Definitionen: int t0 = B::i; int t1 = B::C::a; int t2 = B::C::D::a;
Wenn nach einer using-Directive zur Nominierung eines Namespaces der Namespace erweitert wird und zusätzliche Member und/oder using-Directives hinzugefügt werden, sind diese zusätzlichen Member und die zusätzlichen Namespaces durch die using-Directive sichtbar (im Gegensatz zur using-Deklaration)
namespace D { int d1; void f(char); } using namespace D; // führt D::d1, D::f, D::d2, D::f, // E::e und E::f in den globalen Namensraum ein! int d1; // OK: kein Konflikt mit D::d1 bei der Deklaration namespace E { int e; void f(int); } namespace D // Namensraumerweiterung { int d2; using namespace E; // transitive using-Direktive void f(int); } void f() { d1++; // Fehler: mehrdeutig ::d1 oder D::d1? ::d1++; // OK D::d1++; // OK d2++; // OK, d2 ist D::d2 e++; // OK: e ist E::e aufgrund transitiver using-Direktive f(1); // Fehler: mehrdeutig: D::f(int) oder E::f(int)? f('a'); // OK: die einzige f(char) ist D::f(char) }
Hinweise
Die using-Direktive
using
namespace
std
;
in jedem Namespace-Bereich führt jeden Namen aus dem Namespace
std
in den globalen Namespace ein (da der globale Namespace der nächstgelegene Namespace ist, der sowohl
std
als auch jeden benutzerdefinierten Namespace enthält), was zu unerwünschten Namenskonflikten führen kann. Dies und andere using-Direktiven werden im Allgemeinen als schlechte Praxis im Dateibereich einer Header-Datei betrachtet (
SF.7: Verwenden Sie
using
namespace
nicht im globalen Bereich einer Header-Datei
).
| Feature-Test-Makro | Wert | Std | Feature |
|---|---|---|---|
__cpp_namespace_attributes
|
201411L
|
(C++17) | Attribute für Namensräume |
Schlüsselwörter
Beispiel
Dieses Beispiel zeigt, wie ein Namespace verwendet wird, um eine Klasse zu erstellen, die bereits im
std
-Namespace benannt wurde.
#include <vector> namespace vec { template<typename T> class vector { // ... }; } // of vec int main() { std::vector<int> v1; // Standard vector. vec::vector<int> v2; // User defined vector. // v1 = v2; // Error: v1 and v2 are different object's type. { using namespace std; vector<int> v3; // Same as std::vector v1 = v3; // OK } { using vec::vector; vector<int> v4; // Same as vec::vector v2 = v4; // OK } }
Fehlerberichte
Die folgenden verhaltensändernden Fehlerberichte wurden rückwirkend auf zuvor veröffentlichte C++-Standards angewendet.
| DR | Angewendet auf | Verhalten wie veröffentlicht | Korrektes Verhalten |
|---|---|---|---|
| CWG 101 | C++98 |
das Programm ist fehlerhaft, wenn eine Funktionsdeklaration im Namensraum-
bereich oder Blockbereich und eine durch eine using-Deklaration eingeführte Funktion dieselbe Funktion deklarieren (keine Mehrdeutigkeit) |
erlaubt |
| CWG 373 | C++98 |
Die Suche berücksichtigte Namensraumdeklarationen nur für
den letzten Namen im Operanden einer using-Direktive (was suboptimal ist, da Klassen keine Namensräume enthalten können) |
die Suchbeschränkung
gilt für alle Namen in den Operanden von using-Direktiven |
| CWG 460 | C++98 | eine using-Deklaration konnte einen Namensraum benennen | verboten |
| CWG 565 | C++98 |
eine using-Deklaration kann keine Funktion einführen,
die mit einer anderen Funktion im selben Bereich identisch ist, aber die Einschränkung wurde nicht auf Funktionsvorlagen angewendet |
dieselbe Einschränkung auch
auf Funktionsvorlagen anwenden |
| CWG 986 | C++98 | using-Direktive war transitiv für qualifizierte Suche | nur transitiv für unqualifizierte Suche |
| CWG 987 | C++98 |
in einem verschachtelten Namensraum deklarierte Entitäten waren
auch Mitglieder des umschließenden Namensraums |
verschachtelte Bereiche ausgeschlossen |
| CWG 1021 | C++98 |
es war unklar, ob eine Entität, deren Definition
über eine using-Deklaration in einen Namensraum eingeführt wird, als in diesem Namensraum definiert gilt |
nicht in diesem Namensraum definiert |
| CWG 1838 | C++98 |
unqualifizierte Definition in einem äußeren Namensraum
könnte eine Entität definieren, die in einem anderen Namensraum deklariert, aber nicht definiert und durch using eingebunden wurde |
unqualifizierte Definition
bezieht sich immer auf ihren Namensraum |
| CWG 2155 | C++98 |
die Lösung von
CWG Issue 1838
wurde nicht
auf Klassen- und Aufzählungsdeklarationen angewendet |
angewendet |
Siehe auch
| namespace alias | erstellt einen Alias für einen bestehenden Namespace |