Gültigkeitsbereich
Jede Deklaration, die in einem C++-Programm erscheint, ist nur in bestimmten, möglicherweise nicht zusammenhängenden Geltungsbereichen sichtbar.
Innerhalb eines Geltungsbereichs kann die nicht qualifizierte Namenssuche verwendet werden, um einen Namen mit seiner Deklaration zu verknüpfen.
[bearbeiten] Allgemein
Jedes Programm hat einen globalen Geltungsbereich , der das gesamte Programm enthält.
Jeder andere Geltungsbereich S wird durch eines der folgenden eingeführt:
- eine Deklaration
- ein Parameter in einer Parameterliste
- eine Anweisung
- ein Handler
|
(seit C++26) |
S erscheint immer in einem anderen Geltungsbereich, der somit S enthält.
Ein umgebender Geltungsbereich an einem Programm-Punkt ist jeder Geltungsbereich, der ihn enthält; der kleinste solche Geltungsbereich wird als der direkte Geltungsbereich an diesem Punkt bezeichnet.
Ein Geltungsbereich liegt dazwischen einem Programm-Punkt P und einem Geltungsbereich S (der P nicht enthält), wenn er S ist oder S enthält, aber P nicht enthält.
Der Eltern-Geltungsbereich eines jeden Geltungsbereichs S, der kein Template-Parameter-Geltungsbereich ist, ist der kleinste Geltungsbereich, der S enthält und kein Template-Parameter-Geltungsbereich ist.
Sofern nicht anders angegeben
- Eine Deklaration bewohnt den direkten Geltungsbereich an ihrem Lokus.
- Der Ziel-Geltungsbereich einer Deklaration ist der Geltungsbereich, den sie bewohnt.
- Alle Namen, die durch eine Deklaration (neu) eingeführt werden, sind in ihrem Ziel-Geltungsbereich an diese gebunden.
Eine Entität gehört zu einem Geltungsbereich S, wenn S der Ziel-Geltungsbereich einer Deklaration der Entität ist.
// global scope scope // scope S T int x; // ─┐ // program point X // │ { // │ ─┐ { // │ │ ─┐ int y; // │ │ │ // program point Y } // │ │ ─┘ } // ─┘ ─┘
Im obigen Programm
- Der globale Geltungsbereich, der Geltungsbereich
Sund der GeltungsbereichTenthalten den Programm-PunktY.
- Mit anderen Worten, diese drei Geltungsbereiche sind alle umgebende Geltungsbereiche am Programm-Punkt
Y.
- Mit anderen Worten, diese drei Geltungsbereiche sind alle umgebende Geltungsbereiche am Programm-Punkt
- Der globale Geltungsbereich enthält die Geltungsbereiche
SundT, und der GeltungsbereichSenthält den GeltungsbereichT.
- Daher ist der Geltungsbereich
Tder kleinste Geltungsbereich unter allen dreien, was bedeutet,
- Der Geltungsbereich
Tist der direkte Geltungsbereich am Programm-PunktY. - Die Deklaration der Variable y bewohnt den Geltungsbereich
Tan ihrem Lokus. - Der Geltungsbereich
Tist der Ziel-Geltungsbereich der Deklaration von y. - Die Variable y gehört zum Geltungsbereich
T.
- Der Geltungsbereich
- Der Geltungsbereich
Sist der Eltern-Geltungsbereich von GeltungsbereichT, und der globale Geltungsbereich ist der Eltern-Geltungsbereich von GeltungsbereichS.
- Daher ist der Geltungsbereich
- Der Geltungsbereich
Sliegt zwischen dem Programm-PunktXund dem GeltungsbereichT.
[bearbeiten] Block-Geltungsbereich
Jede
- Auswahlanweisung (if, switch),
- Iterationsanweisung (for, range-for(seit C++11), while, do-while),
- Handler oder
- Verbundanweisung, die keine Verbundanweisung eines Handlers ist
führt einen Block-Geltungsbereich ein, der die Anweisung oder den Handler umfasst.
Eine Variable, die zu einem Block-Geltungsbereich gehört, ist eine Blockvariable .
int i = 42; int a[10]; for (int i = 0; i < 10; i++) // inner “i” inhabits the block scope a[i] = i; // introduced by the for-statement int j = i; // j = 42
Wenn die Deklaration einen Block-Geltungsbereich S bewohnt und eine Funktion deklariert oder den extern-Spezifizierer verwendet, darf die Deklaration nicht an ein benanntes Modul gebunden sein(seit C++20), ist ihr Ziel-Geltungsbereich ein größerer umgebender Geltungsbereich (der innerste umgebende Namespace-Geltungsbereich), aber der Name wird in ihrem direkten Geltungsbereich S gebunden.
Wenn eine Deklaration, die keine namensunabhängige Deklaration ist und(seit C++26), die einen Namen im Block-Geltungsbereich S von
- der Verbundanweisung eines Funktionskörpers oder eines Funktions-try-Blocks,
|
(seit C++11) |
- einer Teil-Anweisung einer Auswahl- oder Iterationsanweisung, die selbst keine Auswahl- oder Iterationsanweisung ist, oder
- einem Handler eines Funktions-try-Blocks
potenziell kollidiert mit einer Deklaration, deren Ziel-Geltungsbereich der Eltern-Geltungsbereich von S ist, ist das Programm fehlerhaft.
if (int x = f()) // declares “x” { // the if-block is a substatement of the if-statement int x; // error: redeclaration of “x” } else { // the else-block is also a substatement of the if-statement int x; // error: redeclaration of “x” } void g(int i) { extern int i; // error: redeclaration of “i” }
[bearbeiten] Funktionsparameter-Geltungsbereich
Jede Parameterdeklaration P führt einen Funktionsparameter-Geltungsbereich ein, der P umfasst.
- Wenn der deklarierte Parameter Teil der Parameterliste einer Funktionsdeklaration ist
- Wenn die Funktionsdeklaration eine Funktionsdefinition ist, wird der eingeführte Geltungsbereich bis zum Ende der Funktionsdefinition erweitert.
- Andernfalls (die Funktionsdeklaration ist ein Funktionsprototyp), wird der eingeführte Geltungsbereich bis zum Ende des Funktionsdeklarators erweitert.
- In beiden Fällen umfasst der Geltungsbereich nicht den Lokus der Funktionsdeklaration.
|
(seit C++11) |
|
(seit C++17) |
|
(seit C++20) |
int f(int n) // the declaration of the parameter “n” { // introduces a function parameter scope /* ... */ } // the function parameter scope ends here
Lambda-GeltungsbereichJeder Lambda-Ausdruck führt einen Lambda-Geltungsbereich ein, der unmittelbar nach Die Captures mit Initialisierungen eines Lambda-Ausdrucks E bewohnen den Lambda-Geltungsbereich, der von E eingeführt wird. auto lambda = [x = 1, y]() // this lambda expression introduces a lambda scope, { // it is the target scope of capture “x” /* ... */ }; // the lambda scope ends before the semicolon |
(seit C++14) |
[bearbeiten] Namespace-Geltungsbereich
Jede Namespace-Definition für einen Namespace N führt einen Namespace-Geltungsbereich S ein, der die Deklarationen jeder Namespace-Definition für N umfasst.
Für jede Nicht-Friend-Neudeklaration oder Spezialisierung, deren Ziel-Geltungsbereich S ist oder von S umschlossen wird, sind die folgenden Teile ebenfalls in Geltungsbereich S enthalten:
- Für eine Klassen- (oder Klassenvorlagen-) Neudeklaration oder Klassenvorlagenspezialisierung, der Teil nach ihrem class-head-name.
- Für eine Aufzählungs- Neudeklaration, der Teil nach ihrem enum-head-name.
- Für jede andere Neudeklaration oder Spezialisierung, der Teil nach dem unqualified-id oder qualified-id des Deklarators.
Der globale Geltungsbereich ist der Namespace-Geltungsbereich des globalen Namespaces.
namespace V // the namespace definition of “V” { // introduces a namespace scope “S” // the first part of scope “S” begins here void f(); // the first part of scope “S” ends here } void V::f() // the portion after “f” is also a part of scope “S” { void h(); // declares V::h } // the second part of scope “S” ends here
[bearbeiten] Klassen-Geltungsbereich
Jede Deklaration einer Klasse oder Klassenvorlage C führt einen Klassen-Geltungsbereich S ein, der die member-specification der Klassendefinition von C umfasst.
Für jede Nicht-Friend-Neudeklaration oder Spezialisierung, deren Ziel-Geltungsbereich S ist oder von S umschlossen wird, sind die folgenden Teile ebenfalls in Geltungsbereich S enthalten:
- Für eine Klassen- (oder Klassenvorlagen-) Neudeklaration oder Klassenvorlagenspezialisierung, der Teil nach ihrem class-head-name.
- Für eine Aufzählungs- Neudeklaration, der Teil nach ihrem enum-head-name.
- Für jede andere Neudeklaration oder Spezialisierung, der Teil nach dem unqualified-id oder qualified-id des Deklarators.
class C // the class definition of “C” { // introduces a class scope “S” // the first part of scope “S” begins here void f(); // the first part of scope “S” ends here } void C::f() // the portion after “f” is also a part of scope “S” { /* ... */ } // the second part of scope “S” ends here
[bearbeiten] Aufzählungs-Geltungsbereich
Jede Deklaration einer Aufzählung E führt einen Aufzählungs-Geltungsbereich ein, der die enumerator-list der Nicht-Opaken(seit C++11) Aufzählungsdeklaration von E (falls vorhanden) umfasst.
enum class E // the enumeration declaration of “E” { // introduces an enumeration scope “S” // scope “S” begins here e1, e2, e3 // scope “S” ends here }
[bearbeiten] Template-Parameter-Geltungsbereich
Jeder Template-Template-Parameter führt einen Template-Parameter-Geltungsbereich ein, der die gesamte Template-Parameterliste und die requires-Klauseln(seit C++20) dieses Template-Template-Parameters umfasst.
Jede Template-Deklaration D führt einen Template-Parameter-Geltungsbereich S ein, der vom Beginn der Template-Parameterliste von D bis zum Ende von D reicht. Jede Deklaration außerhalb der Template-Parameterliste, die S bewohnen würde, bewohnt stattdessen denselben Geltungsbereich wie D.
Nur Template-Parameter gehören zu einem Template-Parameter-Geltungsbereich, und nur Template-Parameter-Geltungsbereiche haben einen Template-Parameter-Geltungsbereich als Eltern-Geltungsbereich.
// the class template declaration of “X” // introduces a template parameter scope “S1” template < // scope “S1” begins here template // the template template parameter “T” // introduces another template parameter scope “S2” < typename T1 typename T2 > requires std::convertible_from<T1, T2> // scope “S2” ends here class T, typename U > class X; // scope “S1” ends before the semicolon namespace N { template <typename T> using A = struct X; // “X” inhabits the same scope as template // declaration, namely the scope of “N” }
Vertrags-Assertion-GeltungsbereichJede Vertrags-Assertion Wenn eine Nachbedingungs-Assertion einen Identifikator hat, der nicht namensunabhängig ist, und die Nachbedingungs-Assertion mit einer Funktion func verbunden ist, die potenziell kollidiert mit einer Deklaration
|
(seit C++26) |
[bearbeiten] Punkt der Deklaration
Im Allgemeinen ist ein Name nach dem Lokus seiner ersten Deklaration sichtbar, der sich wie folgt befindet:
Der Lokus eines Namens, der in einer einfachen Deklaration deklariert wird, liegt unmittelbar nach dem Deklarator dieses Namens und vor seinem Initialisierer, falls vorhanden.
int x = 32; // outer x is in scope { int x = x; // inner x is in scope before the initializer (= x) // this does not initialize inner x with the value of outer x (32), // this initializes inner x with its own (indeterminate) value } std::function<int(int)> f = [&](int n){ return n > 1 ? n * f(n - 1) : n; }; // the name of the function f is in scope in the lambda and can // be correctly captured by reference, giving a recursive function
const int x = 2; // outer x is in scope { int x[x] = {}; // inner x is in scope before the initializer (= {}), // but after the declarator (x[x]) // in the declarator, outer x is still in scope // this declares an array of 2 int }
Der Lokus einer Klassen- oder Klassenvorlagendeklaration liegt unmittelbar nach dem Bezeichner, der die Klasse (oder die template-id, die die Templatespezialisierung benennt) in ihrem Klassen-Head benennt. Der Klassen- oder Klassenvorlagenname ist in der Liste der Basisklassen bereits im Geltungsbereich.
struct S: std::enable_shared_from_this<S> {}; // S is in scope at the colon
Der Lokus eines Aufzählungsspezifizierers oder einer opaken Aufzählungsdeklaration(seit C++11) liegt unmittelbar nach dem Bezeichner, der die Aufzählung benennt.
enum E : int // E is in scope at the colon { A = sizeof(E) };
Der Lokus einer Typ-Alias- oder Aliasvorlagendeklaration liegt unmittelbar nach dem Type-Id, auf das sich der Alias bezieht.
using T = int; // outer T is in scope at the semicolon { using T = T*; // inner T is in scope at the semicolon, // outer T is still in scope before the semicolon // same as T = int* }
Der Lokus für einen Deklarator in einer using-Deklaration, die keinen Konstruktor benennt, liegt unmittelbar nach dem Deklarator.
template<int N> class Base { protected: static const int next = N + 1; static const int value = N; }; struct Derived: Base<0>, Base<1>, Base<2> { using Base<0>::next, // next is in scope at the comma Base<next>::value; // Derived::value is 1 };
Der Lokus eines Enumerators liegt unmittelbar nach seiner Definition (nicht vor dem Initialisierer, wie es bei Variablen der Fall ist).
const int x = 12; { enum { x = x + 1, // enumerator x is in scope at the comma, // outer x is in scope before the comma, // enumerator x is initialized to 13 y = x + 1 // y is initialized to 14 }; }
Der Lokus eines eingeschleusten Klassennamens liegt unmittelbar nach der öffnenden geschweiften Klammer seiner Klassen- (oder Klassenvorlagen-) Definition.
template<typename T> struct Array // : std::enable_shared_from_this<Array> // error: the injected class name is not in scope : std::enable_shared_from_this< Array<T> > // OK: the template-name Array is in scope { // the injected class name Array is now in scope as if a public member name Array* p; // pointer to Array<T> };
|
Der Lokus der impliziten Deklaration für eine funktionslokale vordefinierte Variable __func__ liegt unmittelbar vor dem Funktionskörper einer Funktionsdefinition. |
(seit C++11) |
|
Der Lokus einer strukturierten Bindungsdeklaration liegt unmittelbar nach der identifier-list, aber strukturierte Bindungsinitialisierer dürfen keine der deklarierten Namen referenzieren. |
(seit C++17) |
|
Der Lokus der Variable oder der strukturierten Bindungen(seit C++17), die in der range-declaration einer range-for-Schleife deklariert werden, liegt unmittelbar nach dem range-expression. std::vector<int> x; for (auto x : x) // vector x is in scope before the closing parenthesis, // auto x is in scope at the closing parenthesis { // the auto x is in scope } |
(seit C++11) |
Der Lokus eines Template-Parameters liegt unmittelbar nach seinem vollständigen Template-Parameter (einschließlich des optionalen Standardarguments).
typedef unsigned char T; template< class T = T, // template parameter T is in scope at the comma, // typedef name of unsigned char is in scope before the comma T // template parameter T is in scope N = 0 > struct A { };
|
Der Lokus einer Nachbedingungs-Assertion mit einem Identifikator liegt unmittelbar nach ihrem |
(seit C++26) |
|
Der Lokus einer Konzeptdefinition liegt unmittelbar nach dem Konzeptnamen, aber Konzeptdefinitionen dürfen den zu deklarierenden Konzeptnamen nicht referenzieren. |
(seit C++20) |
Der Lokus einer benannten Namespace-Definition liegt unmittelbar nach dem Namespace-Namen.
| Dieser Abschnitt ist unvollständig Grund: Rest von [basic.scope.pdecl] |
[bearbeiten] Fehlerberichte
Die folgenden Verhaltensändernden Fehlerberichte wurden rückwirkend auf zuvor veröffentlichte C++-Standards angewendet.
| DR | angewendet auf | Verhalten wie veröffentlicht | Korrigiertes Verhalten |
|---|---|---|---|
| CWG 2793 | C++98 | eine extern-Deklaration in einem Block-Geltungsbereich könnte mit einer anderen Deklaration im Eltern-Geltungsbereich kollidieren |
verboten |
[bearbeiten] Referenzen
- C++23 Standard (ISO/IEC 14882:2024)
- 6.4 Scope [basic.scope]
- C++20 Standard (ISO/IEC 14882:2020)
- 6.4 Scope [basic.scope]
- C++17 Standard (ISO/IEC 14882:2017)
- 6.3 Scope [basic.scope]
- C++14 Standard (ISO/IEC 14882:2014)
- 3.3 Scope [basic.scope]
- C++11 Standard (ISO/IEC 14882:2011)
- 3.3 Scope [basic.scope]
- C++98 Standard (ISO/IEC 14882:1998)
- 3.3 Deklarationsbereiche und Geltungsbereiche [basic.scope]
[bearbeiten] Siehe auch
| C-Dokumentation für Geltungsbereich
|