Namespaces
Variants

Scope

From es.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

Cada declaración que aparece en un programa C++ solo es visible en algunos ámbitos  posiblemente discontinuos.

Dentro de un ámbito, unqualified name lookup puede utilizarse para asociar un nombre con su declaración.

Contenidos

General

Cada programa tiene un ámbito global  , que contiene el programa completo.

Cada otro ámbito S es introducido por uno de los siguientes:

(desde C++26)

S siempre aparece en otro ámbito, que por lo tanto contiene S .

Un ámbito envolvente en un punto del programa es cualquier ámbito que lo contiene; el ámbito más pequeño de este tipo se denomina ámbito inmediato en ese punto.

Un ámbito interviene entre un punto de programa P y un ámbito S (que no contiene P ) si es o contiene S pero no contiene P .

El ámbito padre de cualquier ámbito S que no sea un ámbito de parámetro de plantilla es el ámbito más pequeño que contiene S y no es un ámbito de parámetro de plantilla.

A menos que se especifique lo contrario:

  • Una declaración habita el ámbito inmediato en su locus .
  • El ámbito objetivo de una declaración es el ámbito que habita.
  • Cualquier nombre (re)introducido por una declaración queda vinculado a ella en su ámbito objetivo.

Una entidad pertenece a un ámbito S si S es el ámbito destino de una declaración de la entidad.

//                ámbito  ámbito  ámbito
//                global    S      T
int x;         //   ─┐                 // punto del programa X
               //    │
{              //    │     ─┐
    {          //    │      │     ─┐
        int y; //    │      │      │   // punto del programa Y
    }          //    │      │     ─┘
}              //   ─┘     ─┘

En el programa anterior:

  • El ámbito global, el ámbito S y el ámbito T contienen el punto de programa Y .
  • En otras palabras, estos tres ámbitos son todos ámbitos envolventes en el punto del programa Y .
  • El ámbito global contiene los ámbitos S y T , y el ámbito S contiene el ámbito T .
  • Por lo tanto, el ámbito T es el más pequeño entre los tres, lo que significa:
  • El ámbito T es el ámbito inmediato en el punto del programa Y .
  • La declaración de la variable y habita el ámbito T en su locus.
  • El ámbito T es el ámbito objetivo de la declaración de y .
  • La variable y pertenece al ámbito T .
  • El ámbito S es el ámbito padre del ámbito T , y el ámbito global es el ámbito padre del ámbito S .
  • Alcance S interviene entre el punto de programa X y el alcance T .

Ámbito de bloque

Cada

introduce un block scope que incluye la sentencia o el manejador.

Una variable que pertenece a un ámbito de bloque es una variable de bloque  (también conocida como variable local).

int i = 42;
int a[10];
for (int i = 0; i < 10; i++) // la "i" interna habita el ámbito de bloque
    a[i] = i;                // introducida por la sentencia for
int j = i; // j = 42

Si la declaración habita un ámbito de bloque S y declara una función o utiliza el especificador extern , la declaración no debe estar adjunta a un módulo nombrado  (desde C++20) , su ámbito destino es un ámbito envolvente más amplio (el ámbito de espacio de nombres envolvente más interno), pero el nombre está vinculado en su ámbito inmediato S .

Si una declaración que no es una declaración independiente del nombre y (since C++26) que vincula un nombre en el ámbito de bloque S de

(desde C++11)
  • una subdeclaración de una declaración de selección o iteración que no es en sí misma una declaración de selección o iteración, o
  • un manejador de un bloque try de función

potencialmente entra en conflicto con una declaración cuyo ámbito objetivo es el ámbito padre de S , el programa está mal formado.

if (int x = f())  // declara "x"
{ // el bloque if es una subdeclaración de la declaración if
    int x;        // error: redeclaración de "x"
}
else
{ // el bloque else también es una subdeclaración de la declaración if
    int x;        // error: redeclaración de "x"
}
void g(int i)
{
    extern int i; // error: redeclaración de "i"
}

Ámbito de los parámetros de función

Cada declaración de parámetro P introduce un ámbito de parámetro de función que incluye P .

  • Si la declaración de la función es una definición de función , el ámbito introducido se extiende hasta el final de la definición de la función.
  • En caso contrario (la declaración de la función es un prototipo de función), el ámbito introducido se extiende hasta el final del declarador de función.
  • En ambos casos, el ámbito no incluye el locus de la declaración de la función.
  • Si el parámetro declarado pertenece a la lista de parámetros de una expresión lambda , el ámbito introducido se extiende hasta el final de { body } .
(desde C++11)
  • Si el parámetro declarado pertenece a la lista de parámetros de una guía de deducción , el ámbito introducido se extiende hasta el final de dicha guía de deducción.
(desde C++17)
  • Si el parámetro declarado pertenece a la lista de parámetros de una requires expresión , el ámbito introducido se extiende hasta el final de { requirement-seq } .
(desde C++20)
int f(int n) // la declaración del parámetro “n”
{            // introduce un ámbito de parámetro de función
    /* ... */
}            // el ámbito del parámetro de función termina aquí

Ámbito lambda

Cada expresión lambda introduce un ámbito lambda que comienza inmediatamente después de [ capturas  ] y se extiende hasta el final de { cuerpo } .

Las capturas con inicializadores de una expresión lambda E habitan el ámbito lambda introducido por E .

auto lambda = [x = 1, y]() // esta expresión lambda introduce un ámbito lambda,
{                          // es el ámbito destino de la captura “x”
    /* ... */
};                         // el ámbito lambda termina antes del punto y coma
(desde C++14)

Ámbito del espacio de nombres

Cada definición de namespace para un namespace N introduce un ámbito de namespace S que incluye las declaraciones para cada definición de namespace de N .

Para cada redeclaración o especialización no amiga cuyo ámbito objetivo sea S o esté contenido por S , las siguientes porciones también se incluyen en el ámbito S :

  • Para una redeclaración de class (template) o especialización de plantilla de clase, la porción posterior a su class-head-name .
  • Para una redeclaración de enumeration , la porción posterior a su enum-head-name .
  • Para cualquier otra redeclaración o especialización, la porción posterior al unqualified-id o qualified-id del declarator .

El ámbito global es el ámbito de namespace del espacio de nombres global .

namespace V   // la definición del namespace “V”
{             // introduce un ámbito de namespace “S”
    // la primera parte del ámbito “S” comienza aquí
    void f();
    // la primera parte del ámbito “S” termina aquí
}
void V::f()   // la porción después de “f” también es parte del ámbito “S”
{
    void h(); // declara V::h
}             // la segunda parte del ámbito “S” termina aquí

Ámbito de clase

Cada declaración de una clase o plantilla de clase C introduce un ámbito de clase S que incluye la especificación de miembros de la definición de clase de C .

Para cada redeclaración o especialización no amiga cuyo ámbito objetivo sea S o esté contenido por S , las siguientes porciones también se incluyen en el ámbito S :

  • Para una redeclaración de class (template) o especialización de plantilla de clase, la porción posterior a su class-head-name .
  • Para una redeclaración de enumeration , la porción posterior a su enum-head-name .
  • Para cualquier otra redeclaración o especialización, la porción posterior al unqualified-id o qualified-id del declarator .
class C       // la definición de clase de "C"
{             // introduce un ámbito de clase "S"
    // la primera parte del ámbito "S" comienza aquí
    void f();
    // la primera parte del ámbito "S" termina aquí
}
void C::f()   // la porción después de "f" también es parte del ámbito "S"
{
    /* ... */
}             // la segunda parte del ámbito "S" termina aquí

Ámbito de enumeración

Cada declaración de una enumeración E introduce un ámbito de enumeración que incluye la lista-de-enumeradores de la no opaca (desde C++11) declaración de enumeración de E (si está presente).

enum class E // la declaración de enumeración de “E”
{            // introduce un ámbito de enumeración “S”
    // el ámbito “S” comienza aquí
    e1, e2, e3
    // el ámbito “S” termina aquí
}

Ámbito de parámetros de plantilla

Cada parámetro de plantilla de plantilla introduce un ámbito de parámetro de plantilla que incluye la lista completa de parámetros de plantilla y las require clauses (desde C++20) de ese parámetro de plantilla de plantilla.

Cada declaración de plantilla D introduce un ámbito de parámetro de plantilla S que se extiende desde el inicio de la lista de parámetros de plantilla de D hasta el final de D . Cualquier declaración fuera de la lista de parámetros de plantilla que habitaría S en su lugar habita el mismo ámbito que D .

Solo los parámetros de plantilla pertenecen a un ámbito de parámetros de plantilla, y solo los ámbitos de parámetros de plantilla tienen un ámbito de parámetros de plantilla como ámbito padre.

// la declaración de plantilla de clase de “X”
// introduce un ámbito de parámetro de plantilla “S1”
template
<
    // el ámbito “S1” comienza aquí
    template // el parámetro de plantilla “T”
             // introduce otro ámbito de parámetro de plantilla “S2”
    <
        typename T1
        typename T2
    > requires std::convertible_from<T1, T2> // el ámbito “S2” termina aquí
    class T,
    typename U
>
class X; // el ámbito “S1” termina antes del punto y coma
namespace N
{
    template <typename T>
    using A = struct X; // “X” habita en el mismo ámbito que la declaración
                        // de plantilla, concretamente el ámbito de “N”
}

Ámbito de aserción de contrato

Cada aserción de contrato C introduce un ámbito de aserción de contrato que incluye C .

Si una aserción de postcondición tiene un identificador que no es independiente del nombre , y la aserción de postcondición está asociada con una función func potencialmente entra en conflicto con una declaración D cuyo ámbito objetivo es uno de los siguientes ámbitos, el programa está mal formado:

  • El ámbito de parámetros de función de func .
  • Si D está asociado con una expresión lambda , el ámbito lambda de cierre más cercano de la aserción de precondición.
(desde C++26)

Punto de declaración

En general, un nombre es visible después del locus de su primera declaración, que se localiza de la siguiente manera.

El locus de un nombre declarado en una declaración simple está inmediatamente después del declarador de ese nombre y antes de su inicializador, si lo hay.

int x = 32; // la x externa está en ámbito
{
    int x = x; // la x interna está en ámbito antes del inicializador (= x)
               // esto no inicializa la x interna con el valor de la x externa (32),
               // esto inicializa la x interna con su propio valor (indeterminado)
}
std::function<int(int)> f = [&](int n){ return n > 1 ? n * f(n - 1) : n; };
// el nombre de la función f está en ámbito en la lambda y puede
// ser capturado correctamente por referencia, dando una función recursiva
const int x = 2; // la x externa está en ámbito
{
    int x[x] = {}; // la x interna está en ámbito antes del inicializador (= {}),
                   // pero después del declarador (x[x])
                   // en el declarador, la x externa sigue en ámbito
                   // esto declara un array de 2 int
}

El locus de una declaración de clase o plantilla de clase está inmediatamente después del identificador que nombra la clase (o el template-id que nombra la especialización de la plantilla) en su class-head . El nombre de la clase o plantilla de clase ya está en el ámbito en la lista de clases base.

struct S: std::enable_shared_from_this<S> {}; // S está en ámbito en los dos puntos

El locus de enum specifier o declaración de opaque enum (desde C++11) es inmediatamente después del identificador que nombra la enumeración.

enum E : int // E está en el ámbito en los dos puntos
{
    A = sizeof(E)
};

El locus de una declaración de alias de tipo o plantilla de alias está inmediatamente después del type-id al que se refiere el alias.

using T = int; // el T externo está en ámbito en el punto y coma
{
    using T = T*; // el T interno está en ámbito en el punto y coma,
                  // el T externo sigue en ámbito antes del punto y coma
                  // equivalente a T = int*
}

El locus para un declarador en una using declaration que no nombra un constructor está inmediatamente después del declarador.

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á en el ámbito en la coma
          Base<next>::value; // Derived::value es 1
};

El locus de un enumerador está inmediatamente después de su definición (no antes del inicializador como ocurre con las variables).

const int x = 12;
{
    enum
    {
        x = x + 1, // el enumerador x está en ámbito en la coma,
                   // la x externa está en ámbito antes de la coma,
                   // el enumerador x se inicializa a 13
        y = x + 1  // y se inicializa a 14
    };
}

El locus para un injected-class-name está inmediatamente después de la llave de apertura de su definición de clase (o plantilla de clase).

template<typename T>
struct Array
//  : std::enable_shared_from_this<Array> // error: el nombre de clase inyectado no está en el ámbito
    : std::enable_shared_from_this< Array<T> > // OK: el nombre de plantilla Array está en el ámbito
{ // el nombre de clase inyectado Array está ahora en el ámbito como si fuera un nombre de miembro público
    Array* p; // puntero a Array<T>
};

El locus de la declaración implícita para una variable predefinida local a la función __func__ es inmediatamente antes del cuerpo de la función en una definición de función.

(since C++11)


El locus de una declaración de enlace estructurado está inmediatamente después de la lista-de-identificadores , pero los inicializadores de enlace estructurado tienen prohibido referirse a cualquiera de los nombres que se están declarando.

(desde C++17)


El ámbito de la variable o las vinculaciones estructuradas (desde C++17) declarada en la declaración-de-rango de un bucle range- for está inmediatamente después de la expresión-de-rango .

std::vector<int> x;
for (auto x : x) // el vector x está en ámbito antes del paréntesis de cierre,
                 // auto x está en ámbito en el paréntesis de cierre
{
    // el auto x está en ámbito
}
(desde C++11)

El locus de un parámetro de plantilla está inmediatamente después de su parámetro de plantilla completo (incluyendo el argumento predeterminado opcional).

typedef unsigned char T;
template<
    class T = T, // el parámetro de plantilla T está en el ámbito en la coma,
                 // el nombre typedef de unsigned char está en el ámbito antes de la coma
    T // el parámetro de plantilla T está en el ámbito
    N = 0
>
struct A
{
};

El locus de una aserción de postcondición con un identificador es inmediatamente después de su : .

(since C++26)


El locus de una definición de concepto es inmediatamente después del nombre del concepto, pero las definiciones de concepto tienen prohibido referirse al nombre del concepto que se está declarando.

(since C++20)

El locus de una definición de namespace con nombre es inmediatamente después del nombre del namespace.

Informes de defectos

Los siguientes informes de defectos que modifican el comportamiento se aplicaron retroactivamente a los estándares de C++ publicados anteriormente.

DR Se aplica a Comportamiento publicado Comportamiento correcto
CWG 2793 C++98 una declaración extern en un ámbito de bloque podría
entrar en conflicto con otra declaración en el ámbito padre
prohibido

Referencias

  • Estándar C++23 (ISO/IEC 14882:2024):
  • 6.4 Alcance [basic.scope]
  • Estándar C++20 (ISO/IEC 14882:2020):
  • 6.4 Alcance [basic.scope]
  • Estándar C++17 (ISO/IEC 14882:2017):
  • 6.3 Ámbito [basic.scope]
  • Estándar C++14 (ISO/IEC 14882:2014):
  • 3.3 Alcance [basic.scope]
  • Estándar C++11 (ISO/IEC 14882:2011):
  • 3.3 Scope [basic.scope]
  • Estándar C++98 (ISO/IEC 14882:1998):
  • 3.3 Regiones declarativas y ámbitos [basic.scope]

Véase también