Los especificadores de clase de almacenamiento son parte del decl-specifier-seq de la sintaxis de declaración de un nombre. Junto con el alcance del nombre, controlan dos propiedades independientes del nombre: su duración de almacenamiento y es enlace.

  • autoautomático duración del almacenamiento.
(hasta C ++ 11)
  • registerautomático duración del almacenamiento. También sugiere al compilador que coloque el objeto en el registro del procesador. (obsoleto)
(hasta C ++ 17)
  • staticestático o hilo duración del almacenamiento y interno enlace.
  • externestático o hilo duración del almacenamiento y externo enlace.
  • thread_localhilo duración del almacenamiento.
(desde C ++ 11)
  • mutable – no afecta la duración del almacenamiento ni la vinculación. Consulte const / volatile para obtener una explicación.

Solo puede aparecer un especificador de clase de almacenamiento en una declaración excepto eso thread_local puede combinarse con static o con extern(desde C ++ 11).

Explicación

1) los auto El especificador solo se permitió para objetos declarados en el alcance del bloque o en las listas de parámetros de función. Indicó la duración del almacenamiento automático, que es la predeterminada para este tipo de declaraciones. El significado de esta palabra clave se cambió en C ++ 11. (hasta C ++ 11)
2) los register El especificador solo está permitido para objetos declarados en el alcance del bloque y en las listas de parámetros de función. Indica la duración del almacenamiento automático, que es la predeterminada para este tipo de declaraciones. Además, la presencia de esta palabra clave puede usarse como una sugerencia para que el optimizador almacene el valor de esta variable en un registro de CPU. Esta palabra clave quedó obsoleta en C ++ 11. (hasta C ++ 17)

3) los static El especificador solo se permite en las declaraciones de objetos (excepto en las listas de parámetros de funciones), declaraciones de funciones (excepto en el alcance del bloque) y declaraciones de uniones anónimas. Cuando se usa en una declaración de un miembro de clase, declara un miembro estático. Cuando se utiliza en una declaración de un objeto, especifica la duración del almacenamiento estático (excepto si va acompañado de thread_local). Cuando se usa en una declaración en el ámbito del espacio de nombres, especifica el enlace interno.4) los extern El especificador solo se permite en las declaraciones de variables y funciones (excepto miembros de clase o parámetros de función). Especifica el enlace externo y técnicamente no afecta la duración del almacenamiento, pero no se puede utilizar en una definición de un objeto de duración del almacenamiento automático, por lo que todos extern los objetos tienen duraciones estáticas o de subprocesos. Además, una declaración de variable que usa extern y no tiene inicializador no es una definición.

5) los thread_local La palabra clave solo se permite para objetos declarados en el ámbito del espacio de nombres, objetos declarados en el ámbito del bloque y miembros de datos estáticos. Indica que el objeto tiene una duración de almacenamiento de subprocesos. Puede combinarse con static o extern para especificar el enlace interno o externo (excepto para los miembros de datos estáticos que siempre tienen un enlace externo), respectivamente, pero que adicional static no afecta la duración del almacenamiento. (desde C ++ 11)

Duración de almacenamiento

Todos los objetos de un programa tienen una de las siguientes duraciones de almacenamiento:

  • automático duración del almacenamiento. El almacenamiento para el objeto se asigna al comienzo del bloque de código adjunto y se desasigna al final. Todos los objetos locales tienen esta duración de almacenamiento, excepto los declarados static, extern o thread_local.
    • estático duración del almacenamiento. El almacenamiento para el objeto se asigna cuando comienza el programa y se desasigna cuando finaliza el programa. Solo existe una instancia del objeto. Todos los objetos declarados en el ámbito del espacio de nombres (incluido el espacio de nombres global) tienen esta duración de almacenamiento, más los declarados con static o extern. Consulte Variables no locales y Variables locales estáticas para obtener detalles sobre la inicialización de objetos con esta duración de almacenamiento.
  • hilo duración del almacenamiento. El almacenamiento para el objeto se asigna cuando comienza el subproceso y se desasigna cuando finaliza el subproceso. Cada hilo tiene su propia instancia del objeto. Solo objetos declarados thread_local tienen esta duración de almacenamiento. thread_local puede aparecer junto con static o extern para ajustar el enlace. Consulte Variables no locales para obtener detalles sobre la inicialización de objetos con esta duración de almacenamiento.
(desde C ++ 11)
  • dinámica duración del almacenamiento. El almacenamiento para el objeto se asigna y desasigna por solicitud mediante el uso de funciones de asignación de memoria dinámica. Consulte new-expression para obtener detalles sobre la inicialización de objetos con esta duración de almacenamiento.

Enlace

Un nombre que denota objeto, referencia, función, tipo, plantilla, espacio de nombres o valor, puede tener enlace. Si un nombre tiene vinculación, se refiere a la misma entidad que el mismo nombre introducido por una declaración en otro ámbito. Si una variable, función u otra entidad con el mismo nombre se declara en varios ámbitos, pero no tiene la vinculación suficiente, se generan varias instancias de la entidad.

Se reconocen los siguientes vínculos:

  • sin vinculación. Solo se puede hacer referencia al nombre desde el ámbito en el que se encuentra.
    • variables que no se declaran explícitamente extern (a pesar de static modificador);
    • clases locales y sus funciones miembro;
    • otros nombres declarados en el ámbito del bloque, como typedefs, enumeraciones y enumeradores.
  • enlace interno. Se puede hacer referencia al nombre desde todos los ámbitos de la unidad de traducción actual.
    • variables, plantillas variables(desde C ++ 14), funciones o plantillas de funciones declaradas static;
    • No volátil sin plantilla(desde C ++ 14)no en línea(desde C ++ 17)variables calificadas const (incluido constexpr) que no están declaradas extern y no se ha declarado previamente que tengan vinculación externa;
    • datos de miembros de uniones anónimas.
Además, todos los nombres declarados en un espacio de nombres sin nombre o un espacio de nombres dentro de un espacio de nombres sin nombre, incluso los declarados explícitamente extern, tienen vinculación interna. (desde C ++ 11)
  • enlace externo. Se puede hacer referencia al nombre desde los ámbitos de las otras unidades de traducción. Las variables y funciones con enlace externo también tienen enlace de lenguaje, lo que hace posible vincular 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 un vínculo externo a menos que el espacio de nombres no tenga nombre o esté contenido dentro de un espacio de nombres sin nombre(desde C ++ 11):

  • variables y funciones no enumeradas anteriormente (es decir, funciones no declaradas static, variables no constantes del ámbito del espacio de nombres no declaradas static, y cualquier variable declarada extern);
  • enumeraciones;
  • nombres de clases, sus funciones miembro, miembros de datos estáticos (const o no), enumeraciones y clases anidadas, y funciones introducidas por primera vez con declaraciones de amigos dentro de los cuerpos de las clases;
  • nombres de todas las plantillas no enumeradas anteriormente (es decir, no las plantillas de funciones declaradas static).
Cualquiera de los siguientes nombres declarados por primera vez en el alcance del bloque tiene un enlace externo:

  • nombres de variables declaradas extern;
  • nombres de funciones.

Variables locales estáticas

Variables declaradas en el alcance del bloque con el especificador static tienen una duración de almacenamiento estático pero se inicializan la primera vez que el control pasa por su declaración (a menos que su inicialización sea cero o constante, que se puede realizar antes de que se ingrese por primera vez al bloque). En todas las llamadas posteriores, se omite la declaración.

Si la inicialización arroja 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 de forma recursiva en el bloque en el que se inicializa la variable, el comportamiento es indefinido.

Si varios subprocesos intentan inicializar la misma variable local estática al mismo tiempo, la inicialización se produce exactamente una vez (se puede obtener un comportamiento similar para funciones arbitrarias con std::call_once).

Nota: las implementaciones habituales de esta función utilizan variantes del patrón de bloqueo doble verificado, que reduce la sobrecarga del tiempo de ejecución para las estáticas locales ya inicializadas a una única comparación booleana no atómica.

(desde C ++ 11)

El destructor de una variable estática de alcance de bloque se llama al salir del programa, pero solo si la inicialización se realizó correctamente.

Los objetos estáticos locales de función en todas las definiciones de la misma función en línea (que pueden estar implícitamente en línea) hacen referencia al mismo objeto definido en una unidad de traducción.

Notas

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 la deducción de tipo.

En C, la dirección de un register variable no se puede tomar, pero en C ++, una variable declarada register es semánticamente indistinguible de una variable declarada sin ningún especificador de clase de almacenamiento.

(hasta C ++ 17)

En C ++, a diferencia de C, las variables no se pueden declarar register.

(desde C ++ 17)

Nombres de thread_local las variables con enlace interno o externo referidas desde diferentes ámbitos pueden referirse a la misma instancia oa diferentes instancias dependiendo de si el código se está ejecutando en el mismo hilo o en diferentes hilos.

los extern La palabra clave también se puede usar para especificar el enlace de idioma y las declaraciones de instanciación de plantilla explícitas, 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 enlace de idioma, en la que caso de que la declaración sea tratada como si contuviera el extern especificador).

La palabra clave mutable es un especificador de clase de almacenamiento en la gramática del lenguaje C ++, aunque no afecta la duración del almacenamiento o la vinculación.

Especificadores de clase de almacenamiento, excepto para thread_local, no están permitidos en especializaciones explícitas e instancias explícitas:

template<classT>structSthread_localstaticint tlm;;template<>thread_localint S<float>::tlm =0;// "static" does not appear here

Palabras clave

auto, register, static, extern, thread_local, mutable.

Ejemplo

#include#include#include#includethread_localunsignedint rage =1; 
std::mutex cout_mutex;voidincrease_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';intmain()
    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();

Posible salida:

Rage counter for a:2
Rage counter for main:1
Rage counter for b:2

Informes de defectos

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

DR Aplicado a Comportamiento según lo publicado Comportamiento correcto
CWG 2387 C ++ 14 no está claro si la plantilla de variable calificada const
tener enlace interno por defecto
calificador const no afecta la vinculación de
plantillas de variables o sus instancias

Ver también