Scope
Chaque déclaration qui apparaît dans un programme C++ n'est visible que dans certaines portées potentiellement non contiguës.
Dans une portée, la recherche de nom non qualifiée peut être utilisée pour associer un nom à sa déclaration.
Table des matières |
Général
Chaque programme possède une portée globale , qui contient l'intégralité du programme.
Toute autre portée
S
est introduite par l'un des éléments suivants :
- une déclaration
- un paramètre dans une liste de paramètres
- une instruction
- un gestionnaire
| (depuis C++26) |
S
apparaît toujours dans une autre portée, qui par conséquent
contient
S
.
Un scope englobant à un point du programme est toute portée qui le contient ; la plus petite portée de ce type est appelée scope immédiat à ce point.
Une portée
intervient
entre un point de programme
P
et une portée
S
(qui ne contient pas
P
) si elle est ou contient
S
mais ne contient pas
P
.
La
portée parente
de toute portée
S
qui n'est pas une
portée de paramètre de template
est la plus petite portée qui contient
S
et qui n'est pas une portée de paramètre de template.
Sauf indication contraire :
- Une déclaration habite la portée immédiate à son locus .
- La portée cible d'une déclaration est la portée qu'elle habite.
- Tous les noms (ré)introduits par une déclaration y sont liés dans sa portée cible.
Une entité
appartient
à une portée
S
si
S
est la portée cible d'une déclaration de l'entité.
// portée portée portée // globale S T int x; // ─┐ // point de programme X // │ { // │ ─┐ { // │ │ ─┐ int y; // │ │ │ // point de programme Y } // │ │ ─┘ } // ─┘ ─┘
Dans le programme ci-dessus :
-
La portée globale, la portée
Set la portéeTcontiennent le point de programmeY.
-
-
En d'autres termes, ces trois portées sont toutes des portées englobantes au point de programme
Y.
-
En d'autres termes, ces trois portées sont toutes des portées englobantes au point de programme
-
La portée globale contient les portées
SetT, et la portéeScontient la portéeT.
-
-
Par conséquent, la portée
Test la plus petite parmi les trois, ce qui signifie :
-
-
La portée
Test la portée immédiate au point de programmeY. -
La déclaration de la variable
y
habite la portée
Tà son locus. -
La portée
Test la portée cible de la déclaration de y . -
La variable
y
appartient à la portée
T.
-
La portée
-
La portée
Sest la portée parente de la portéeT, et la portée globale est la portée parente de la portéeS.
-
Par conséquent, la portée
-
Portée
Sintervient entre le point de programmeXet la portéeT.
Portée de bloc
Chaque
- instruction de sélection ( if , switch ),
- instruction d'itération ( for , for sur plage for (depuis C++11) , while , do - while ),
- gestionnaire , ou
- instruction composée qui n'est pas l' instruction-composée d'un gestionnaire
introduit une portée de bloc qui inclut l'instruction ou le gestionnaire.
Une variable qui appartient à une portée de bloc est une variable de bloc (également appelée variable locale).
int i = 42; int a[10]; for (int i = 0; i < 10; i++) // le « i » interne habite la portée du bloc a[i] = i; // introduit par l'instruction for int j = i; // j = 42
Si la déclaration se trouve dans une portée de bloc
S
et déclare une fonction ou utilise le spécificateur
extern
, la déclaration ne doit pas être attachée à un
module nommé
(depuis C++20)
, sa portée cible est une portée englobante plus large (la portée de l'espace de noms englobant le plus proche), mais le nom est lié dans sa portée immédiate
S
.
Si une déclaration
qui n'est pas une
déclaration indépendante du nom
et
(depuis C++26)
qui lie un nom dans la portée de bloc
S
de
- la compound-statement d'un corps de fonction ou d'un bloc try de fonction ,
|
(depuis C++11) |
- une sous-instruction d'une instruction de sélection ou d'itération qui n'est pas elle-même une instruction de sélection ou d'itération, ou
- un gestionnaire d'un bloc try de fonction
entre potentiellement en conflit
avec une déclaration dont la portée cible est la portée parente de
S
, le programme est mal formé.
if (int x = f()) // déclare « x » { // le bloc if est une sous-instruction de l'instruction if int x; // erreur : redéclaration de « x » } else { // le bloc else est également une sous-instruction de l'instruction if int x; // erreur : redéclaration de « x » } void g(int i) { extern int i; // erreur : redéclaration de « i » }
Portée des paramètres de fonction
Chaque
déclaration de paramètre
P
introduit une
portée de paramètre de fonction
qui inclut
P
.
- Si le paramètre déclaré fait partie de la liste de paramètres d'une déclaration de fonction :
-
- Si la déclaration de fonction est une définition de fonction , la portée introduite s'étend jusqu'à la fin de la définition de fonction.
- Sinon (la déclaration de fonction est un prototype de fonction), la portée introduite s'étend jusqu'à la fin du déclarateur de fonction.
- Dans les deux cas, la portée n'inclut pas le locus de la déclaration de fonction.
|
(depuis C++11) |
|
(depuis C++17) |
|
(depuis C++20) |
int f(int n) // la déclaration du paramètre « n » { // introduit une portée de paramètre de fonction /* ... */ } // la portée du paramètre de fonction se termine ici
Portée lambda
Chaque
expression lambda
introduit une
portée lambda
qui commence immédiatement après
Les captures avec initialiseurs d'une expression lambda E résident dans la portée lambda introduite par E . auto lambda = [x = 1, y]() // cette expression lambda introduit une portée lambda, { // c'est la portée cible de la capture « x » /* ... */ }; // la portée lambda se termine avant le point-virgule |
(depuis C++14) |
Portée de l'espace de noms
Chaque
définition d'espace de noms
pour un espace de noms
N
introduit une
portée d'espace de noms
S
qui inclut les
déclarations
de chaque définition d'espace de noms pour
N
.
Pour chaque redéclaration ou spécialisation non-amie dont la portée cible est
S
ou est contenue par
S
, les parties suivantes sont également incluses dans la portée
S
:
- Pour une classe (template) redéclarée ou une spécialisation de template de classe, la partie suivant son class-head-name .
- Pour une énumération redéclarée, la partie suivant son enum-head-name .
- Pour toute autre redéclaration ou spécialisation, la partie suivant le unqualified-id ou le qualified-id du déclarateur .
La portée globale est la portée de l'espace de noms du global namespace .
namespace V // la définition du namespace « V » { // introduit une portée de namespace « S » // la première partie de la portée « S » commence ici void f(); // la première partie de la portée « S » se termine ici } void V::f() // la partie après « f » fait également partie de la portée « S » { void h(); // déclare V::h } // la deuxième partie de la portée « S » se termine ici
Portée de la classe
Chaque déclaration d'une classe ou d'un modèle de classe
C
introduit une
portée de classe
S
qui inclut la
spécification des membres
de la
définition de classe
de
C
.
Pour chaque redéclaration ou spécialisation non-amie dont la portée cible est
S
ou est contenue par
S
, les parties suivantes sont également incluses dans la portée
S
:
- Pour une classe (template) redéclarée ou une spécialisation de template de classe, la partie suivant son class-head-name .
- Pour une énumération redéclarée, la partie suivant son enum-head-name .
- Pour toute autre redéclaration ou spécialisation, la partie suivant le unqualified-id ou le qualified-id du déclarateur .
class C // la définition de classe de « C » { // introduit une portée de classe « S » // la première partie de la portée « S » commence ici void f(); // la première partie de la portée « S » se termine ici } void C::f() // la partie après « f » fait également partie de la portée « S » { /* ... */ } // la deuxième partie de la portée « S » se termine ici
Portée de l'énumération
Chaque déclaration d'une énumération
E
introduit une
portée d'énumération
qui inclut la
liste d'énumérateurs
de la
déclaration d'énumération non opaque
(depuis C++11)
déclaration d'énumération
de
E
(si présente).
enum class E // la déclaration d'énumération de « E » { // introduit une portée d'énumération « S » // la portée « S » commence ici e1, e2, e3 // la portée « S » se termine ici }
Portée des paramètres de template
Chaque paramètre de template template introduit une portée de paramètre de template qui inclut la liste complète des paramètres de template et les require clauses (depuis C++20) de ce paramètre de template template.
Chaque déclaration de modèle
D
introduit une
portée de paramètre de modèle
S
qui s'étend du début de la liste de paramètres de modèle de
D
jusqu'à la fin de
D
. Toute déclaration en dehors de la liste de paramètres de modèle qui habiterait
S
habite plutôt la même portée que
D
.
Seuls les paramètres de template appartiennent à une portée de paramètre de template, et seules les portées de paramètre de template ont une portée de paramètre de template comme portée parente.
// la déclaration du modèle de classe « X » // introduit une portée de paramètre de modèle « S1 » template < // la portée « S1 » commence ici template // le paramètre de modèle de modèle « T » // introduit une autre portée de paramètre de modèle « S2 » < typename T1 typename T2 > requires std::convertible_from<T1, T2> // la portée « S2 » se termine ici class T, typename U > class X; // la portée « S1 » se termine avant le point-virgule namespace N { template <typename T> using A = struct X; // « X » habite la même portée que la déclaration // de modèle, à savoir la portée de « N » }
Portée de l'assertion de contrat
Chaque
assertion de contrat
Si une
assertion de postcondition
possède un
identifiant
qui n'est pas
indépendant du nom
, et que l'assertion de postcondition est associée à une fonction
func
entre potentiellement en conflit
avec une déclaration
|
(depuis C++26) |
Point de déclaration
En général, un nom est visible après le locus de sa première déclaration, qui est situé comme suit.
Le locus d'un nom déclaré dans une déclaration simple se situe immédiatement après le déclarateur de ce nom et avant son initialiseur, s'il y en a un.
int x = 32; // le x externe est dans la portée { int x = x; // le x interne est dans la portée avant l'initialiseur (= x) // cela n'initialise pas le x interne avec la valeur du x externe (32), // cela initialise le x interne avec sa propre valeur (indéterminée) } std::function<int(int)> f = [&](int n){ return n > 1 ? n * f(n - 1) : n; }; // le nom de la fonction f est dans la portée dans le lambda et peut // être correctement capturé par référence, donnant une fonction récursive
const int x = 2; // le x externe est dans la portée { int x[x] = {}; // le x interne est dans la portée avant l'initialiseur (= {}), // mais après le déclarateur (x[x]) // dans le déclarateur, le x externe est toujours dans la portée // ceci déclare un tableau de 2 entiers }
Le lieu de déclaration d'une classe ou d'un modèle de classe se situe immédiatement après l'identificateur qui nomme la classe (ou l' identifiant-de-modèle qui nomme la spécialisation du modèle) dans son en-tête-de-classe . Le nom de la classe ou du modèle de classe est déjà dans la portée dans la liste des classes de base.
struct S: std::enable_shared_from_this<S> {}; // S est dans la portée au niveau du deux-points
Le locus de enum specifier ou opaque enum declaration (since C++11) est immédiatement après l'identifiant qui nomme l'énumération.
enum E : int // E est dans la portée au niveau du deux-points { A = sizeof(E) };
Le locus d'une déclaration de type alias ou alias template est immédiatement après le type-id auquel l'alias se réfère.
using T = int; // le T externe est dans la portée au point-virgule { using T = T*; // le T interne est dans la portée au point-virgule, // le T externe est toujours dans la portée avant le point-virgule // équivalent à T = int* }
Le locus pour un déclarateur dans une déclaration using qui ne nomme pas un constructeur est immédiatement après le déclarateur.
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 est dans la portée à la virgule Base<next>::value; // Derived::value vaut 1 };
Le locus d'un énumérateur se situe immédiatement après sa définition (et non avant l'initialiseur comme c'est le cas pour les variables).
const int x = 12; { enum { x = x + 1, // l'énumérateur x est dans la portée à la virgule, // le x externe est dans la portée avant la virgule, // l'énumérateur x est initialisé à 13 y = x + 1 // y est initialisé à 14 }; }
Le locus pour un injected-class-name est immédiatement après l'accolade ouvrante de sa définition de classe (ou de modèle de classe).
template<typename T> struct Array // : std::enable_shared_from_this<Array> // erreur : le nom de classe injecté n'est pas dans la portée : std::enable_shared_from_this< Array<T> > // OK : le nom de template Array est dans la portée { // le nom de classe injecté Array est maintenant dans la portée comme un nom de membre public Array* p; // pointeur vers Array<T> };
|
Le lieu de la déclaration implicite pour une variable prédéfinie locale à une fonction __func__ est immédiatement avant le corps de la fonction dans une définition de fonction. |
(since C++11) |
|
Le locus d'une déclaration de liaison structurée est immédiatement après la liste-d'identifiants , mais les initialiseurs de liaison structurée sont interdits de faire référence à l'un des noms étant déclarés. |
(depuis C++17) |
|
Le locus de la variable ou des liaisons structurées (depuis C++17) déclarée dans la range-declaration d'une boucle range- for est immédiatement après la range-expression . std::vector<int> x; for (auto x : x) // le vecteur x est dans la portée avant la parenthèse fermante, // auto x est dans la portée à la parenthèse fermante { // auto x est dans la portée } |
(depuis C++11) |
Le locus d'un paramètre de template est immédiatement après son paramètre de template complet (incluant l'argument par défaut optionnel).
typedef unsigned char T; template< class T = T, // le paramètre de template T est dans la portée à la virgule, // le nom typedef de unsigned char est dans la portée avant la virgule T // le paramètre de template T est dans la portée N = 0 > struct A { };
|
Le locus d'une
assertion de postcondition
avec un
identifiant
est immédiatement après son
|
(since C++26) |
|
Le locus d'une définition de concept est immédiatement après le nom du concept, mais les définitions de concept sont interdites de faire référence au nom du concept en cours de déclaration. |
(since C++20) |
Le locus d'une définition de namespace nommé est immédiatement après le nom du namespace.
|
Cette section est incomplète
Raison : reste de [basic.scope.pdecl] |
Rapports de défauts
Les rapports de défauts modifiant le comportement suivants ont été appliqués rétroactivement aux normes C++ précédemment publiées.
| DR | S'applique à | Comportement tel que publié | Comportement correct |
|---|---|---|---|
| CWG 2793 | C++98 |
une déclaration
extern
dans une portée de bloc pouvait
entrer en conflit avec une autre déclaration dans la portée parente |
interdit |
Références
- Norme C++23 (ISO/CEI 14882:2024) :
-
- 6.4 Portée [basic.scope]
- Norme C++20 (ISO/IEC 14882:2020) :
-
- 6.4 Portée [basic.scope]
- Norme C++17 (ISO/CEI 14882:2017) :
-
- 6.3 Portée [basic.scope]
- Norme C++14 (ISO/CEI 14882:2014) :
-
- 3.3 Portée [basic.scope]
- Norme C++11 (ISO/IEC 14882:2011) :
-
- 3.3 Portée [basic.scope]
- Norme C++98 (ISO/CEI 14882:1998) :
-
- 3.3 Régions déclaratives et portées [basic.scope]
Voir aussi
|
Documentation C
pour
Scope
|