Scope
Jede Deklaration , die in einem C++-Programm erscheint, ist nur in einigen möglicherweise nicht zusammenhängenden Gültigkeitsbereichen sichtbar.
Innerhalb eines Geltungsbereichs kann unqualified name lookup verwendet werden, um einen Namen mit seiner Deklaration zu verknüpfen.
Allgemein
Jedes Programm hat einen global scope , der enthält das gesamte Programm.
Jeder andere Gültigkeitsbereich
S
wird durch eines der folgenden Elemente eingeführt:
- eine Deklaration
- ein Parameter in einer Parameterliste
- eine Anweisung
- ein Handler
|
(seit C++26) |
S
erscheint immer in einem anderen Gültigkeitsbereich, der dadurch
enthält
S
.
Ein enclosing scope an einem Programmpunkt ist jeder Gültigkeitsbereich, der ihn enthält; der kleinste solche Gültigkeitsbereich wird als immediate scope an diesem Punkt bezeichnet.
Ein Geltungsbereich
interveniert
zwischen einem Programmpunkt
P
und einem Geltungsbereich
S
(der
P
nicht enthält), wenn er
S
ist oder enthält, aber
P
nicht enthält.
Der
parent scope
eines beliebigen Scopes
S
, der kein
template parameter scope
ist, ist der kleinste Scope, der
S
enthält und kein template parameter scope ist.
Sofern nicht anders angegeben:
- Eine Deklaration belegt den unmittelbaren Gültigkeitsbereich an ihrem Locus .
- Der Zielgültigkeitsbereich einer Deklaration ist der Gültigkeitsbereich, den sie belegt.
- Alle durch eine Deklaration (wieder)eingeführten Namen werden in ihrem Zielgültigkeitsbereich an sie gebunden .
Eine Entität
gehört
zu einem Geltungsbereich
S
, wenn
S
der Ziel-Geltungsbereich einer Deklaration der Entität ist.
// globaler Gültigkeitsbereich Gültigkeitsbereich // Gültigkeitsbereich S T int x; // ─┐ // Programm-Punkt X // │ { // │ ─┐ { // │ │ ─┐ int y; // │ │ │ // Programm-Punkt Y } // │ │ ─┘ } // ─┘ ─┘
Im obigen Programm:
-
Der globale Gültigkeitsbereich, Gültigkeitsbereich
Sund GültigkeitsbereichTenthalten den ProgrammpunktY.
-
-
Mit anderen Worten, diese drei Gültigkeitsbereiche sind alle umschließende Gültigkeitsbereiche am Programmpunkt
Y.
-
Mit anderen Worten, diese drei Gültigkeitsbereiche sind alle umschließende Gültigkeitsbereiche am Programmpunkt
-
Der globale Bereich enthält die Bereiche
SundT, und BereichSenthält BereichT.
-
-
Daher ist der Geltungsbereich
Tder kleinste aller drei Geltungsbereiche, was bedeutet:
-
-
Der Geltungsbereich
Tist der unmittelbare Geltungsbereich am ProgrammpunktY. -
Die Deklaration der Variable
y
belegt den Geltungsbereich
Tan ihrem Ort. -
Der Geltungsbereich
Tist der Zielgeltungsbereich der Deklaration von y . -
Die Variable
y
gehört zum Geltungsbereich
T.
-
Der Geltungsbereich
-
Der Geltungsbereich
Sist der übergeordnete Geltungsbereich vonT, und der globale Geltungsbereich ist der übergeordnete Geltungsbereich vonS.
-
Daher ist der Geltungsbereich
-
Geltungsbereich
Sgreift zwischen ProgrammpunktXund GeltungsbereichTein.
Blockbereich
Jeder
- Auswahlanweisung ( if , switch ),
- Schleifenanweisung ( for , Bereichs- for (seit C++11) , while , do - while ),
- Handler , oder
- Verbundanweisung , die nicht die Verbundanweisung eines Handlers ist
führt einen block scope ein, der die Anweisung oder den Handler enthält.
Eine Variable, die zu einem Blockbereich gehört, ist eine Blockvariable (auch bekannt als lokale Variable).
int i = 42; int a[10]; for (int i = 0; i < 10; i++) // inner “i” bewohnt den Blockgültigkeitsbereich a[i] = i; // eingeführt durch die for-Anweisung int j = i; // j = 42
Wenn die Deklaration einen Blockbereich
S
bewohnt und eine Funktion deklariert oder den
extern
Spezifizierer verwendet
, darf die Deklaration nicht an ein
benanntes Modul
angehängt werden
(seit C++20)
, ihr Zielbereich ist ein größerer umschließender Bereich (der innerste umschließende Namensbereich), aber der Name wird in ihrem unmittelbaren Bereich
S
gebunden.
Wenn eine Deklaration
die keine
namensunabhängige Deklaration
ist und
(since C++26)
einen Namen im Blockgültigkeitsbereich
S
bindet
- die compound-statement eines Funktionskörpers oder eines Funktions- try -Blocks ,
|
(seit C++11) |
- ein Unterstatement einer Auswahl- oder Iterationsanweisung, das selbst keine Auswahl- oder Iterationsanweisung ist, oder
- ein Handler eines Funktions try -Blocks
potenziell in Konflikt steht
mit einer Deklaration, deren Zielbereich der übergeordnete Bereich von
S
ist, ist das Programm fehlerhaft.
if (int x = f()) // deklariert "x" { // der if-Block ist ein Unterstatement des if-Statements int x; // Fehler: Neudeklaration von "x" } else { // der else-Block ist ebenfalls ein Unterstatement des if-Statements int x; // Fehler: Neudeklaration von "x" } void g(int i) { extern int i; // Fehler: Neudeklaration von "i" }
Funktionsparameterbereich
Jede
Parameterdeklaration
P
führt einen
Funktionsparameterbereich
ein, der
P
einschließt.
- Wenn der deklarierte Parameter zur Parameterliste einer Funktionsdeklaration gehört:
-
- Wenn die Funktionsdeklaration eine Funktionsdefinition ist, erstreckt sich der eingeführte Geltungsbereich bis zum Ende der Funktionsdefinition.
- Andernfalls (wenn die Funktionsdeklaration ein Funktionsprototyp ist), erstreckt sich der eingeführte Geltungsbereich bis zum Ende des Funktionsdeklarators.
- In beiden Fällen umfasst der Geltungsbereich nicht den Locus der Funktionsdeklaration.
|
(seit C++11) |
|
(seit C++17) |
|
(seit C++20) |
int f(int n) // die Deklaration des Parameters "n" { // führt einen Funktionsparameter-Bereich ein /* ... */ } // der Funktionsparameter-Bereich endet hier
Lambda-Bereich
Jeder
Lambda-Ausdruck
führt einen
Lambda-Bereich
ein, der unmittelbar nach
Die captures mit Initialisierern eines Lambda-Ausdrucks E befinden sich im Lambda-Bereich, der durch E eingeführt wird. auto lambda = [x = 1, y]() // dieser Lambda-Ausdruck führt einen Lambda-Bereich ein, { // er ist der Zielbereich des Captures "x" /* ... */ }; // der Lambda-Bereich endet vor dem Semikolon |
(seit C++14) |
Namespace-Bereich
Jede
Namespace-Definition
für einen Namespace
N
führt einen
Namespace-Bereich
S
ein, der die
Deklarationen
für jede Namespace-Definition für
N
enthält.
Für jede Nicht-Freund-Neudeklaration oder Spezialisierung, deren Zielbereich
S
ist oder in
S
enthalten ist, werden die folgenden Teile ebenfalls in den Bereich
S
eingeschlossen:
- Für eine Klasse (Template)-Neudeklaration oder Klassentemplate-Spezialisierung, den Teil nach ihrem class-head-name .
- Für eine Enumeration -Neudeklaration, den Teil nach ihrem enum-head-name .
- Für jede andere Neudeklaration oder Spezialisierung, den Teil nach dem unqualified-id oder qualified-id des Deklarators .
Der globale Gültigkeitsbereich ist der Namensraumgültigkeitsbereich des globalen Namensraums .
namespace V // die Namespace-Definition von "V" { // führt einen Namespace-Bereich "S" ein // der erste Teil des Bereichs "S" beginnt hier void f(); // der erste Teil des Bereichs "S" endet hier } void V::f() // der Teil nach "f" ist ebenfalls Teil des Bereichs "S" { void h(); // deklariert V::h } // der zweite Teil des Bereichs "S" endet hier
Klassenbereich
Jede Deklaration einer Klasse oder eines Klassentemplates
C
führt einen
Klassenbereich
S
ein, der die
Member-Spezifikation
der
Klassendefinition
von
C
umfasst.
Für jede Nicht-Freund-Neudeklaration oder Spezialisierung, deren Zielbereich
S
ist oder in
S
enthalten ist, werden die folgenden Teile ebenfalls in den Bereich
S
eingeschlossen:
- Für eine class (Template)-Neudeklaration oder Klassentemplate-Spezialisierung, den Teil nach ihrem class-head-name .
- Für eine enumeration -Neudeklaration, den Teil nach ihrem enum-head-name .
- Für jede andere Neudeklaration oder Spezialisierung, den Teil nach der unqualified-id oder qualified-id des declarator .
class C // die Klassendefinition von "C" { // führt einen Klassenbereich "S" ein // der erste Teil des Bereichs "S" beginnt hier void f(); // der erste Teil des Bereichs "S" endet hier } void C::f() // der Teil nach "f" ist ebenfalls Teil des Bereichs "S" { /* ... */ } // der zweite Teil des Bereichs "S" endet hier
Aufzählungsbereich
Jede Deklaration einer Enumeration
E
führt einen
Enumeration-Scope
ein, der die
Enumerator-Liste
der
nicht-opaken
(seit C++11)
Enumeration-Deklaration
von
E
(falls vorhanden) enthält.
enum class E // die Enumerationsdeklaration von "E" { // führt einen Enumerationsbereich "S" ein // Bereich "S" beginnt hier e1, e2, e3 // Bereich "S" endet hier }
Templateparameter-Bereich
Jeder Template-Template-Parameter führt einen Template-Parameter-Bereich ein, der die gesamte Template-Parameterliste und die require -Klauseln (seit C++20) dieses Template-Template-Parameters umfasst.
Jede Template-Deklaration
D
führt einen
Template-Parameter-Bereich
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 Bereich wie
D
.
Nur Template-Parameter gehören zu einem Template-Parameter-Bereich, und nur Template-Parameter-Bereiche haben einen Template-Parameter-Bereich als übergeordneten Bereich.
// Die Klassentemplate-Deklaration von "X" // führt einen Template-Parameterbereich "S1" ein template < // Bereich "S1" beginnt hier template // Der Template-Template-Parameter "T" // führt einen weiteren Template-Parameterbereich "S2" ein < typename T1 typename T2 > requires std::convertible_from<T1, T2> // Bereich "S2" endet hier class T, typename U > class X; // Bereich "S1" endet vor dem Semikolon namespace N { template <typename T> using A = struct X; // "X" befindet sich im selben Bereich wie die Template- // Deklaration, nämlich im Bereich von "N" }
Vertragsassertions-Bereich
Jede
Contract-Assertion
Wenn eine
Nachbedingungs-Assertion
einen
Bezeichner
besitzt, der nicht
namensunabhängig
ist, und die Nachbedingungs-Assertion mit einer Funktion
func
assoziiert ist,
potenziell kollidiert
mit einer Deklaration
|
(seit C++26) |
Punkt der Deklaration
Im Allgemeinen ist ein Name nach dem locus seiner ersten Deklaration sichtbar, der wie folgt lokalisiert wird.
Der Geltungsbereich eines Namens, der in einer einfachen Deklaration deklariert wird, beginnt unmittelbar nach dem Deklarator dieses Namens und vor seinem Initialisierer, falls vorhanden.
int x = 32; // äußeres x ist im Gültigkeitsbereich { int x = x; // inneres x ist im Gültigkeitsbereich vor dem Initialisierer (= x) // dies initialisiert das innere x nicht mit dem Wert des äußeren x (32), // dies initialisiert das innere x mit seinem eigenen (undefinierten) Wert } std::function<int(int)> f = [&](int n){ return n > 1 ? n * f(n - 1) : n; }; // der Name der Funktion f ist im Lambda-Ausdruck im Gültigkeitsbereich und kann // korrekt per Referenz erfasst werden, was eine rekursive Funktion ergibt
const int x = 2; // äußeres x ist im Gültigkeitsbereich { int x[x] = {}; // inneres x ist im Gültigkeitsbereich vor dem Initialisierer (= {}), // aber nach dem Deklarator (x[x]) // im Deklarator ist das äußere x weiterhin im Gültigkeitsbereich // dies deklariert ein Array von 2 int }
Der Ort einer Klasse oder Klassentemplate-Deklaration ist unmittelbar nach dem Bezeichner, der die Klasse benennt (oder der template-id , die die Template-Spezialisierung benennt) in ihrem class-head . Der Klassen- oder Klassentemplate-Name ist bereits im Gültigkeitsbereich in der Liste der Basisklassen.
struct S: std::enable_shared_from_this<S> {}; // S ist beim Doppelpunkt im Gültigkeitsbereich
Der Ort des enum-Spezifizierers oder der opaken Enum-Deklaration (seit C++11) ist unmittelbar nach dem Bezeichner, der die Aufzählung benennt.
enum E : int // E ist im Gültigkeitsbereich beim Doppelpunkt { A = sizeof(E) };
Der Ort einer Typalias- oder Aliastemplate Deklaration ist unmittelbar nach der Typ-ID, auf die sich der Alias bezieht.
using T = int; // äußeres T ist am Semikolon im Gültigkeitsbereich { using T = T*; // inneres T ist am Semikolon im Gültigkeitsbereich, // äußeres T ist vor dem Semikolon weiterhin im Gültigkeitsbereich // gleichbedeutend mit T = int* }
Der Geltungsbereich für einen Deklarator in einer using declaration , die keinen Konstruktor benennt, ist 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 ist im Gültigkeitsbereich am Komma Base<next>::value; // Derived::value ist 1 };
Der Ort eines Enumerators befindet sich unmittelbar nach seiner Definition (nicht vor dem Initialisierer wie bei Variablen).
const int x = 12; { enum { x = x + 1, // Enumerator x ist im Gültigkeitsbereich bis zum Komma, // äußeres x ist im Gültigkeitsbereich vor dem Komma, // Enumerator x wird mit 13 initialisiert y = x + 1 // y wird mit 14 initialisiert }; }
Der Ort für einen injected-class-name ist unmittelbar nach der öffnenden Klammer seiner Klasse (oder Klassenvorlage) Definition.
template<typename T> struct Array // : std::enable_shared_from_this<Array> // Fehler: der injizierte Klassenname ist nicht im Gültigkeitsbereich : std::enable_shared_from_this< Array<T> > // OK: der Template-Name Array ist im Gültigkeitsbereich { // der injizierte Klassenname Array ist nun im Gültigkeitsbereich wie ein öffentlicher Mitgliedsname Array* p; // Zeiger auf Array<T> };
|
Der Ort der impliziten Deklaration für eine funktionslokale vordefinierte Variable __func__ ist unmittelbar vor dem Funktionskörper einer Funktionsdefinition. |
(since C++11) |
|
Der Geltungsbereich einer structured binding declaration beginnt unmittelbar nach der identifier-list , aber structured binding Initialisierern ist es untersagt, auf einen der deklarierten Namen zu verweisen. |
(since C++17) |
|
Der Geltungsbereich der Variablen oder der strukturierten Bindungen (seit C++17) , die in der range-declaration einer Range- for -Schleife deklariert werden, beginnt unmittelbar nach dem range-expression . std::vector<int> x; for (auto x : x) // Vektor x ist im Geltungsbereich vor der schließenden Klammer, // auto x ist im Geltungsbereich an der schließenden Klammer { // das auto x ist im Geltungsbereich } |
(seit C++11) |
Der Ort eines Template-Parameters ist unmittelbar nach seinem vollständigen Template-Parameter (einschließlich des optionalen Standardarguments).
typedef unsigned char T; template< class T = T, // Template-Parameter T ist am Komma im Gültigkeitsbereich, // Typdef-Name von unsigned char ist vor dem Komma im Gültigkeitsbereich T // Template-Parameter T ist im Gültigkeitsbereich N = 0 > struct A { };
|
Der Ort einer
Nachbedingungsassertion
mit einem
Bezeichner
ist unmittelbar nach ihrem
|
(since C++26) |
|
Der Geltungsbereich einer Concept-Definition beginnt unmittelbar nach dem Concept-Namen, jedoch ist es Concept-Definitionen untersagt, auf den deklarierten Concept-Namen zu verweisen. |
(since C++20) |
Der Ort einer benannten Namespace-Definition ist unmittelbar nach dem Namespace-Namen.
|
Dieser Abschnitt ist unvollständig
Grund: Rest von [basic.scope.pdecl] |
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 2793 | C++98 |
eine
extern
Deklaration im Blockbereich konnte
mit einer anderen Deklaration im übergeordneten Bereich in Konflikt stehen |
verboten |
Referenzen
- C++23-Standard (ISO/IEC 14882:2024):
-
- 6.4 Gültigkeitsbereich [basic.scope]
- C++20-Standard (ISO/IEC 14882:2020):
-
- 6.4 Gültigkeitsbereich [basic.scope]
- C++17-Standard (ISO/IEC 14882:2017):
-
- 6.3 Gültigkeitsbereich [basic.scope]
- C++14-Standard (ISO/IEC 14882:2014):
-
- 3.3 Gültigkeitsbereich [basic.scope]
- C++11 Standard (ISO/IEC 14882:2011):
-
- 3.3 Gültigkeitsbereich [basic.scope]
- C++98-Standard (ISO/IEC 14882:1998):
-
- 3.3 Deklarative Bereiche und Gültigkeitsbereiche [basic.scope]
Siehe auch
|
C-Dokumentation
für
Scope
|