Scope
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.
General
Cada programa tiene un ámbito global , que contiene el programa completo.
Cada otro ámbito
S
es introducido por uno de los siguientes:
- una declaración
- un parámetro en lista de parámetros
- una sentencia
- un manejador
| (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
Sy el ámbitoTcontienen el punto de programaY.
-
-
En otras palabras, estos tres ámbitos son todos ámbitos envolventes en el punto del programa
Y.
-
En otras palabras, estos tres ámbitos son todos ámbitos envolventes en el punto del programa
-
El ámbito global contiene los ámbitos
SyT, y el ámbitoScontiene el ámbitoT.
-
-
Por lo tanto, el ámbito
Tes el más pequeño entre los tres, lo que significa:
-
-
El ámbito
Tes el ámbito inmediato en el punto del programaY. -
La declaración de la variable
y
habita el ámbito
Ten su locus. -
El ámbito
Tes el ámbito objetivo de la declaración de y . -
La variable
y
pertenece al ámbito
T.
-
El ámbito
-
El ámbito
Ses el ámbito padre del ámbitoT, y el ámbito global es el ámbito padre del ámbitoS.
-
Por lo tanto, el ámbito
-
Alcance
Sinterviene entre el punto de programaXy el alcanceT.
Ámbito de bloque
Cada
- sentencia de selección ( if , switch ),
- sentencia de iteración ( for , for de rango for (desde C++11) , while , do - while ),
- manejador , o
- sentencia compuesta que no sea la sentencia-compuesta de un manejador
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
- la compound-statement de un cuerpo de función o bloque try de función ,
|
(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 el parámetro declarado pertenece a la lista de parámetros de una declaración de función :
-
- 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.
|
(desde C++11) |
|
(desde C++17) |
|
(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
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
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
|
(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.
|
Esta sección está incompleta
Motivo: resto de [basic.scope.pdecl] |
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
|
Documentación de C
para
Scope
|