Storage class specifiers
Los especificadores de clase de almacenamiento son parte de la decl-specifier-seq de la sintaxis de declaración de un nombre. Junto con el ámbito del nombre, controlan dos propiedades independientes del nombre: su duración de almacenamiento y su vinculación .
Contenidos |
Duración de almacenamiento
La duración de almacenamiento es la propiedad de un objeto que define el tiempo de vida mínimo potencial del almacenamiento que contiene el objeto. La duración de almacenamiento está determinada por la construcción utilizada para crear el objeto y es una de las siguientes:
- duración de almacenamiento estático
|
(since C++11) |
- duración de almacenamiento automático
- duración de almacenamiento dinámico
Static , thread, (since C++11) y las duraciones de almacenamiento automático están asociadas con objetos introducidos por declaraciones y con objetos temporales . La duración de almacenamiento dinámico está asociada con objetos creados por una new expression o con objetos creados implícitamente .
Las categorías de duración de almacenamiento se aplican también a las referencias.
La duración de almacenamiento de los subobjects y miembros de referencia es la de su objeto completo.
Especificadores
Las siguientes palabras clave son especificadores de clase de almacenamiento :
|
(hasta C++11) |
|
(hasta C++17) |
- static
|
(desde C++11) |
- extern
- mutable
En un decl-specifier-seq , puede haber como máximo un especificador de clase de almacenamiento , excepto que thread_local puede aparecer con static o extern (desde C++11) .
mutable no tiene efecto en la duración de almacenamiento. Para su uso, consulte const/volatile .
Otros especificadores de clase de almacenamiento pueden aparecer en las decl-specifier-seq s de las siguientes declaraciones:
| Especificador | Puede aparecer en las decl-specifier-seq s de | ||||||||
|---|---|---|---|---|---|---|---|---|---|
| Declaraciones de variables | Declaraciones de funciones |
Declaraciones de enlace estructurado
(desde C++17) |
|||||||
| No miembro | Miembro | No miembro | Miembro | ||||||
| No parámetro | Parámetro de función | No estático | Estático | No estático | Estático | ||||
| auto | Solo ámbito de bloque | Sí | No | No | No | No | No | N/A | |
| register | Solo ámbito de bloque | Sí | No | No | No | No | No | N/A | |
| static | Sí | No | Declara static | Solo ámbito de namespace | Declara static | Sí | |||
| thread_local | Sí | No | No | Sí | No | No | No | Sí | |
| extern | Sí | No | No | No | Sí | No | No | No | |
Uniones anónimas también pueden declararse con static .
|
register es una sugerencia de que la variable así declarada será utilizada intensamente, por lo que su valor puede almacenarse en un registro de la CPU. La sugerencia puede ser ignorada, y en la mayoría de las implementaciones será ignorada si se toma la dirección de la variable. Este uso está obsoleto. |
(until C++17) |
Duración de almacenamiento estático
Una variable que satisface todas las siguientes condiciones tiene static storage duration :
- Pertenece a un ámbito de espacio de nombres o se declara primero con static o extern .
|
(since C++11) |
El almacenamiento para estas entidades dura durante la duración del programa.
Duración de almacenamiento de hiloTodas las variables declaradas con thread_local tienen duración de almacenamiento de hilo . El almacenamiento para estas entidades dura durante la existencia del hilo en el que se crean. Existe un objeto o referencia distinto por cada hilo, y el uso del nombre declarado se refiere a la entidad asociada con el hilo actual. |
(since C++11) |
Duración de almacenamiento automático
Las siguientes variables tienen automatic storage duration :
- Variables que pertenecen a un ámbito de bloque y no están declaradas explícitamente como static , thread_local , (desde C++11) o extern . El almacenamiento para dichas variables dura hasta que el bloque en el que se crean finaliza.
- Variables que pertenecen a un ámbito de parámetro (es decir, parámetros de función). El almacenamiento para un parámetro de función dura hasta inmediatamente después de su destrucción .
Duración de almacenamiento dinámico
Los objetos creados por los siguientes métodos durante la ejecución del programa tienen dynamic storage duration :
- new expressions . El almacenamiento para dichos objetos es asignado por funciones de asignación y liberado por funciones de desasignación .
- Creación implícita por otros medios. El almacenamiento para dichos objetos se superpone con algún almacenamiento existente.
- Objetos de excepción . El almacenamiento para dichos objetos es asignado y liberado de manera no especificada.
Vinculación
Un nombre puede tener enlace externo , enlace de módulo (desde C++20) , enlace interno , o sin enlace :
- Una entidad cuyo nombre tiene vinculación externa puede ser redeclarada en otra unidad de traducción , y la redeclaración puede ser adjuntada a un módulo diferente (desde C++20) .
|
(desde C++20) |
- Una entidad cuyo nombre tiene enlace interno puede ser redeclarada en otro ámbito en la misma unidad de traducción.
- Una entidad cuyo nombre no tiene enlace solo puede ser redeclarada en el mismo ámbito.
Se reconocen los siguientes enlaces:
Sin enlace
Cualquiera de los siguientes nombres declarados en el ámbito de bloque no tiene vinculación:
- variables que no están declaradas explícitamente extern (independientemente del modificador static );
- clases locales y sus funciones miembro;
- otros nombres declarados en ámbito de bloque como typedefs, enumeraciones y enumeradores.
Los nombres no especificados con vinculación externa , de módulo, (since C++20) o interna tampoco tienen vinculación, independientemente del ámbito en el que se declaren.
Enlace interno
Cualquiera de los siguientes nombres declarados en el ámbito de espacio de nombres tiene vinculación interna:
- variables , plantillas de variables (desde C++14) , funciones, o plantillas de funciones declaradas static ;
- no plantilla (desde C++14) variables de tipo calificado const no volátil, a menos que
|
(since C++17) |
|
(since C++20) |
-
- están explícitamente declarados extern , o
- fueron declarados previamente y la declaración anterior no tenía enlace interno;
- miembros de datos de uniones anónimas .
|
Además, todos los nombres declarados en espacios de nombres sin nombre o en un espacio de nombres dentro de un espacio de nombres sin nombre, incluso aquellos declarados explícitamente extern , tienen vinculación interna. |
(desde C++11) |
Enlace externo
Las variables y funciones con enlace externo también tienen language linkage , lo que hace posible enlazar unidades de traducción escritas en diferentes lenguajes de programación.
Cualquiera de los siguientes nombres declarados en el ámbito del espacio de nombres tiene vinculación externa, a menos que estén declarados en un espacio de nombres sin nombre o sus declaraciones estén adjuntas a un módulo con nombre y no sean exportadas (desde C++20) :
- variables y funciones no listadas anteriormente (es decir, funciones no declaradas static , variables no constantes no declaradas static , y cualquier variable declarada extern );
- enumeraciones;
- nombres de clases, sus funciones miembro, miembros de datos estáticos (constantes o no), clases anidadas y enumeraciones, y funciones introducidas por primera vez mediante friend declaraciones dentro de cuerpos de clase;
- nombres de todas las plantillas no listadas anteriormente (es decir, no plantillas de función declaradas static ).
Cualquiera de los siguientes nombres declarados primero en el ámbito de bloque tiene vinculación externa:
- nombres de variables declaradas extern ;
- nombres de funciones.
Vinculación de móduloLos nombres declarados en el ámbito del espacio de nombres tienen vinculación de módulo si sus declaraciones están adjuntas a un módulo con nombre y no se exportan, y no tienen vinculación interna. |
(since C++20) |
|
Esta sección está incompleta
Razón: agregar la descripción del comportamiento cuando una entidad se declara con diferentes vinculaciones en la misma unidad de traducción (párrafo 6.6), notar la diferencia entre C++20 (mal formado) y el borrador actual (bien formado) |
Variables de bloque estáticas
Las variables de bloque con duración de almacenamiento estático o de hilo (desde C++11) se inicializan la primera vez que el control pasa por su declaración (a menos que su inicialización sea zero- o constant-initialization , que puede realizarse antes de que se entre al bloque por primera vez). En todas las llamadas posteriores, la declaración se omite.
- Si la inicialización lanza una excepción , la variable no se considera inicializada, y la inicialización se intentará nuevamente la próxima vez que el control pase por la declaración.
- Si la inicialización entra recursivamente en el bloque en el que la variable está siendo inicializada, el comportamiento es indefinido.
|
(desde C++11) |
El destructor para una variable de bloque con duración de almacenamiento estático se llama al salir del programa , pero solo si la inicialización se realizó exitosamente.
Las variables con duración de almacenamiento estático en todas las definiciones de la misma función inline (que puede ser implícitamente inline) se refieren todas al mismo objeto definido en una unidad de traducción, siempre que la función tenga enlace externo.
Entidades locales a la unidad de traducción
El concepto de entidades locales a la unidad de traducción se estandarizó en C++20, consulte esta página para más detalles.
Una entidad es translation-unit-local (o TU-local para abreviar) si
- tiene un nombre con enlace interno, o
- no tiene un nombre con enlace y se introduce dentro de la definición de una entidad local de la UT, o
- es una plantilla o especialización de plantilla cuyo argumento de plantilla o declaración de plantilla utiliza una entidad local de la UT.
Cosas malas (generalmente violación de ODR ) pueden ocurrir si el tipo de una entidad no-local a la TU depende de una entidad local a la TU, o si una declaración de , o una guía de deducción para, (desde C++17) una entidad no-local a la TU nombra una entidad local a la TU fuera de su
- cuerpo de función para una función no inline o plantilla de función
- inicializador para una variable o plantilla de variable
- declaraciones friend en una definición de clase
- uso del valor de una variable, si la variable es usable en expresiones constantes
|
Dichos usos no están permitidos en una unidad de interfaz de módulo (fuera de su fragmento privado del módulo, si existe) o en una partición de módulo, y están obsoletos en cualquier otro contexto. Una declaración que aparece en una unidad de traducción no puede nombrar una entidad TU-local declarada en otra unidad de traducción que no sea una unidad de cabecera. Una declaración instanciada para una plantilla aparece en el punto de instanciación de la especialización. |
(since C++20) |
Notas
Los nombres en el ámbito del espacio de nombres de nivel superior (ámbito de archivo en C) que son const y no extern tienen enlace externo en C, pero enlace interno en C++.
Desde C++11, auto ya no es un especificador de clase de almacenamiento; se utiliza para indicar deducción de tipos.
|
En C, la dirección de una variable register no puede tomarse, pero en C++, una variable declarada register es semánticamente indistinguible de una variable declarada sin ningún especificador de clase de almacenamiento. |
(until C++17) |
|
En C++, a diferencia de C, las variables no pueden declararse register . |
(since C++17) |
Los nombres de thread_local variables con vinculación interna o externa referidas desde diferentes ámbitos pueden referirse a la misma o a diferentes instancias dependiendo de si el código se ejecuta en el mismo o en diferentes hilos.
La palabra clave extern también puede utilizarse para especificar vinculación de lenguaje y declaraciones de instanciación explícita de plantillas , pero no es un especificador de clase de almacenamiento en esos casos (excepto cuando una declaración está contenida directamente en una especificación de vinculación de lenguaje, en cuyo caso la declaración se trata como si contuviera el especificador extern ).
Los especificadores de clase de almacenamiento, excepto thread_local , no están permitidos en especializaciones explícitas y instanciaciones explícitas :
template<class T> struct S { thread_local static int tlm; }; template<> thread_local int S<float>::tlm = 0; // "static" no aparece aquí
|
Una plantilla de variable const (puede estar implícita por constexpr ) solía tener enlace interno por defecto, lo cual era inconsistente con otras entidades con plantilla. El informe de defectos CWG2387 corrigió esto. |
(since C++14) |
inline
actúa como una solución alternativa para
CWG2387
al proporcionar enlace externo por defecto. Esta es la razón por la cual
inline
fue
añadido
a muchas plantillas de variables y luego
eliminado
después de que se aceptó CWG2387. Las implementaciones de la biblioteca estándar también necesitan usar
inline
mientras un compilador compatible no tenga implementado CWG2387. Ver
GCC Bugzilla #109126
y
MSVC STL PR #4546
.
|
(since C++17) |
| Macro de prueba de características | Valor | Std | Característica |
|---|---|---|---|
__cpp_threadsafe_static_init
|
200806L
|
(C++11) | Inicialización y destrucción dinámica con concurrencia |
Palabras clave
auto , register , static , extern , thread_local , mutable
Ejemplo
#include <iostream> #include <mutex> #include <string> #include <thread> thread_local unsigned int rage = 1; std::mutex cout_mutex; void increase_rage(const std::string& thread_name) { ++rage; // modifying outside a lock is okay; this is a thread-local variable std::lock_guard<std::mutex> lock(cout_mutex); std::cout << "Rage counter for " << thread_name << ": " << rage << '\n'; } int main() { std::thread a(increase_rage, "a"), b(increase_rage, "b"); { std::lock_guard<std::mutex> lock(cout_mutex); std::cout << "Rage counter for main: " << rage << '\n'; } a.join(); b.join(); }
Salida posible:
Rage counter for a: 2 Rage counter for main: 1 Rage counter for b: 2
Informes de defectos
Los siguientes informes de defectos que modifican el comportamiento se aplicaron retroactivamente a los estándares de C++ publicados anteriormente.
| DR | Aplicado a | Comportamiento publicado | Comportamiento correcto |
|---|---|---|---|
| CWG 216 | C++98 |
las clases y enumeraciones sin nombre en ámbito de clase tienen
diferente linkage que aquellas en ámbito de namespace |
todas tienen linkage externo
en estos ámbitos |
| CWG 389 | C++98 |
un nombre sin linkage no debería ser
usado para declarar una entidad con linkage |
un tipo sin linkage no debe ser usado
como tipo de una variable o función con linkage, a menos que la variable o función tenga linkage de lenguaje C |
| CWG 426 | C++98 |
una entidad podría ser declarada con linkage interno
y externo en la misma unidad de traducción |
el programa está mal formado en este caso |
| CWG 527 | C++98 |
la restricción de tipo introducida por la resolución de CWG
389 también se aplicaba a variables y funciones que no pueden ser nombradas fuera de sus propias unidades de traducción |
la restricción se elimina para estas
variables y funciones (es decir, sin linkage o con linkage interno, o declaradas dentro de namespaces sin nombre) |
| CWG 809 | C++98 | register servía para muy poca función | obsoleto |
| CWG 1648 | C++11 |
static
estaba implícito incluso si
thread_local se combina con extern |
implícito solo si no hay otro especificador
de clase de almacenamiento presente |
| CWG 1686 |
C++98
C++11 |
el nombre de una variable no estática declarada en ámbito
de namespace tenía linkage interno solo si era explícitamente declarada const (C++98) o constexpr (C++11) |
solo requiere que el tipo
esté calificado como const |
| CWG 2019 | C++98 |
la duración de almacenamiento de miembros
de referencia no estaba especificada |
igual que su objeto completo |
| CWG 2387 | C++14 |
no estaba claro si las plantillas de variables
calificadas como const tienen linkage interno por defecto |
el calificador const no afecta
el linkage de las plantillas de variables o sus instancias |
| CWG 2533 | C++98 |
la duración de almacenamiento de objetos
creados implícitamente no estaba clara |
se aclaró |
| CWG 2850 | C++98 |
no estaba claro cuándo se desasigna el almacenamiento
para los parámetros de función |
se aclaró |
| CWG 2872 | C++98 | el significado de "puede ser referido" no estaba claro | redacción mejorada |
| P2788R0 | C++20 |
declarar una variable calificada como const en un namespace
le daba linkage interno incluso en una unidad de módulo |
no se le da linkage interno |
Referencias
- Estándar C++23 (ISO/IEC 14882:2024):
-
- 6.7.5 Duración de almacenamiento [basic.stc]
- Estándar C++20 (ISO/IEC 14882:2020):
-
- 6.7.5 Duración de almacenamiento [basic.stc]
- Estándar C++17 (ISO/IEC 14882:2017):
-
- 6.7 Duración de almacenamiento [basic.stc]
- Estándar C++14 (ISO/IEC 14882:2014):
-
- 3.7 Duración de almacenamiento [basic.stc]
- Estándar C++11 (ISO/IEC 14882:2011):
-
- 3.7 Duración de almacenamiento [basic.stc]
- Estándar C++03 (ISO/IEC 14882:2003):
-
- 3.7 Duración de almacenamiento [basic.stc]
- Estándar C++98 (ISO/IEC 14882:1998):
-
- 3.7 Duración de almacenamiento [basic.stc]
Véase también
|
Documentación de C
para
storage duration
|