Namespaces
Variants

Storage class specifiers

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

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
  • duración de almacenamiento de hilo (también conocida como duración de almacenamiento local de hilo)
(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 :

  • auto
(hasta C++11)
  • register
(hasta C++17)
  • static
  • thread_local
(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 No No No No No N/A
register Solo ámbito de bloque No No No No No N/A
static No Declara static Solo ámbito de namespace Declara static
thread_local No No No No No
extern No No No 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 :

  • No tiene duración de almacenamiento de hilo.
(since C++11)

El almacenamiento para estas entidades dura durante la duración del programa.

Duración de almacenamiento de hilo

Todas 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 :

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 de módulo puede ser redeclarada en otra unidad de traducción, siempre que la redeclaración esté adjunta al mismo módulo.
(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
  • son inline,
(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;

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ódulo

Los 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)

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.
  • Si múltiples hilos intentan inicializar la misma variable local estática concurrentemente, la inicialización ocurre exactamente una vez (un comportamiento similar puede obtenerse para funciones arbitrarias con std::call_once ).
  • Las implementaciones usuales de esta característica utilizan variantes del patrón de bloqueo de doble verificación, que reduce la sobrecarga en tiempo de ejecución para estáticas locales ya inicializadas a una única comparación booleana no atómica.
(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