Namespaces
Variants

Scope

From cppreference.net
C++ language
General topics
Flow control
Conditional execution statements
Iteration statements (loops)
Jump statements
Functions
Function declaration
Lambda function expression
inline specifier
Dynamic exception specifications ( until C++17* )
noexcept specifier (C++11)
Exceptions
Namespaces
Types
Specifiers
constexpr (C++11)
consteval (C++20)
constinit (C++20)
Storage duration specifiers
Initialization
Expressions
Alternative representations
Literals
Boolean - Integer - Floating-point
Character - String - nullptr (C++11)
User-defined (C++11)
Utilities
Attributes (C++11)
Types
typedef declaration
Type alias declaration (C++11)
Casts
Memory allocation
Classes
Class-specific function properties
Special member functions
Templates
Miscellaneous

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 :

(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 S et la portée T contiennent le point de programme Y .
  • En d'autres termes, ces trois portées sont toutes des portées englobantes au point de programme Y .
  • La portée globale contient les portées S et T , et la portée S contient la portée T .
  • Par conséquent, la portée T est la plus petite parmi les trois, ce qui signifie :
  • La portée T est la portée immédiate au point de programme Y .
  • La déclaration de la variable y habite la portée T à son locus.
  • La portée T est la portée cible de la déclaration de y .
  • La variable y appartient à la portée T .
  • La portée S est la portée parente de la portée T , et la portée globale est la portée parente de la portée S .
  • Portée S intervient entre le point de programme X et la portée T .

Portée de bloc

Chaque

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

(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 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.
  • Si le paramètre déclaré fait partie de la liste de paramètres d'une expression lambda , la portée introduite s'étend jusqu'à la fin de { corps } .
(depuis C++11)
  • Si le paramètre déclaré fait partie de la liste de paramètres d'un guide de déduction , la portée introduite s'étend jusqu'à la fin de ce guide de déduction.
(depuis C++17)
  • Si le paramètre déclaré fait partie de la liste de paramètres d'une requires expression , la portée introduite s'étend jusqu'à la fin de { séquence-exigences } .
(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 [ captures  ] et s'étend jusqu'à la fin de { corps } .

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 C introduit une portée d'assertion de contrat qui inclut C .

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 D dont la portée cible est l'une des portées suivantes, le programme est mal formé :

  • La portée des paramètres de fonction de func .
  • Si D est associée à une expression lambda , la portée lambda englobante la plus proche de l'assertion de précondition.
(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.

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