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

Les spécificateurs de classe de stockage font partie de la decl-specifier-seq de la syntaxe de déclaration d'un nom. Avec la portée du nom, ils contrôlent deux propriétés indépendantes du nom : sa durée de stockage et son linkage .

Table des matières

Durée de stockage

La durée de stockage est la propriété d'un objet qui définit la durée de vie minimale potentielle du stockage contenant l'objet. La durée de stockage est déterminée par le constructe utilisé pour créer l'objet et est l'une des suivantes :

  • durée de stockage statique
  • durée de stockage des threads (également connue sous le nom de durée de stockage locale aux threads)
(depuis C++11)
  • durée de stockage automatique
  • durée de stockage dynamique

Durées de stockage statique, thread, (depuis C++11) et automatique sont associées aux objets introduits par des déclarations et avec les objets temporaires . La durée de stockage dynamique est associée aux objets créés par une new expression ou avec les objets implicitement créés .

Les catégories de durée de stockage s'appliquent également aux références.

La durée de stockage des sous-objets et des membres de référence est celle de leur objet complet.

Spécificateurs

Les mots-clés suivants sont des storage class specifiers :

  • auto
(jusqu'à C++11)
  • register
(jusqu'à C++17)
  • static
  • thread_local
(depuis C++11)
  • extern
  • mutable

Dans une decl-specifier-seq , il peut y avoir au plus un spécificateur de classe de stockage , sauf que thread_local peut apparaître avec static ou extern (depuis C++11) .

mutable n'a aucun effet sur la durée de stockage. Pour son utilisation, voir const/volatile .

D'autres spécificateurs de classe de stockage peuvent apparaître dans les decl-specifier-seq s des déclarations suivantes :

Spécificateur Peut apparaître dans les decl-specifier-seq de
Déclarations de variables Déclarations de fonctions Déclarations de liaisons structurées
(depuis C++17)
Non-membre Membre Non-membre Membre
Non-paramètre Paramètre de fonction Non-statique Statique Non-statique Statique
auto Portée de bloc uniquement Oui Non Non Non Non Non N/A
register Portée de bloc uniquement Oui Non Non Non Non Non N/A
static Oui Non Déclare statique Portée d'espace de noms uniquement Déclare statique Oui
thread_local Oui Non Non Oui Non Non Non Oui
extern Oui Non Non Non Oui Non Non Non

Les unions anonymes peuvent également être déclarées avec static .

register est une indication que la variable ainsi déclarée sera fortement utilisée, de sorte que sa valeur peut être stockée dans un registre du CPU. Cette indication peut être ignorée, et dans la plupart des implémentations elle sera ignorée si l'adresse de la variable est prise. Cet usage est déprécié.

(jusqu'à C++17)

Durée de stockage statique

Une variable satisfaisant toutes les conditions suivantes possède static storage duration :

  • Il appartient à un namespace scope ou est déclaré pour la première fois avec static ou extern .
  • Il n'a pas de durée de stockage de thread.
(since C++11)

Le stockage de ces entités dure pendant toute la durée du programme.

Durée de stockage des threads

Toutes les variables déclarées avec thread_local ont une durée de stockage des threads .

Le stockage de ces entités dure pendant toute la durée du thread dans lequel elles sont créées. Il existe un objet ou une référence distinct par thread, et l'utilisation du nom déclaré fait référence à l'entité associée au thread actuel.

(depuis C++11)

Durée de stockage automatique

Les variables suivantes ont automatic storage duration :

  • Les variables qui appartiennent à une portée de bloc et ne sont pas explicitement déclarées static , thread_local , (depuis C++11) ou extern . Le stockage de ces variables persiste jusqu'à la sortie du bloc dans lequel elles sont créées.
  • Les variables qui appartiennent à une portée de paramètre (c'est-à-dire les paramètres de fonction). Le stockage d'un paramètre de fonction persiste jusqu'immédiatement après sa destruction .

Durée de stockage dynamique

Les objets créés par les méthodes suivantes pendant l'exécution du programme ont une durée de stockage dynamique :

Liaison

Un nom peut avoir une liaison externe  , une liaison de module (depuis C++20) , une liaison interne , ou aucune liaison :

  • Une entité dont le nom a une liaison de module peut être redéclarée dans une autre unité de traduction, à condition que la redéclaration soit attachée au même module.
(depuis C++20)
  • Une entité dont le nom a une liaison interne peut être redéclarée dans une autre portée de la même unité de traduction.
  • Une entité dont le nom n'a aucune liaison ne peut être redéclarée que dans la même portée.

Les liaisons suivantes sont reconnues :

Aucun lien

Tous les noms suivants déclarés au niveau d'un bloc n'ont aucune liaison :

  • variables qui ne sont pas explicitement déclarées extern (indépendamment du modificateur static );
  • classes locales et leurs fonctions membres ;
  • autres noms déclarés au niveau du bloc tels que les typedefs, les énumérations et les énumérateurs.

Les noms non spécifiés avec une liaison externe , module, (depuis C++20) ou liaison interne n'ont également aucune liaison, indépendamment de la portée dans laquelle ils sont déclarés.

Lien interne

Tous les noms suivants déclarés au niveau de l'espace de noms ont une liaison interne :

  • variables , modèles de variables (depuis C++14) , fonctions, ou modèles de fonctions déclarés static ;
  • non-modèle (depuis C++14) variables de type const qualifié non volatile, sauf si
  • ils sont inline,
(depuis C++17)
(depuis C++20)
  • ils sont explicitement déclarés extern , ou
  • ils ont été précédemment déclarés et la déclaration antérieure n'avait pas de liaison interne ;

De plus, tous les noms déclarés dans les espaces de noms sans nom ou dans un espace de noms à l'intérieur d'un espace de noms sans nom, même ceux explicitement déclarés extern , ont une liaison interne.

(depuis C++11)

Lien externe

Les variables et fonctions avec liaison externe possèdent également une liaison de langage , ce qui permet de lier des unités de traduction écrites dans différents langages de programmation.

Les noms suivants déclarés au niveau de l'espace de noms ont une liaison externe, sauf s'ils sont déclarés dans un espace de noms anonyme ou si leurs déclarations sont attachées à un module nommé et ne sont pas exportées (depuis C++20) :

  • variables et fonctions non listées ci-dessus (c'est-à-dire les fonctions non déclarées static , les variables non constantes non déclarées static , et toute variable déclarée extern );
  • énumérations;
  • noms de classes, leurs fonctions membres, membres de données statiques (constantes ou non), classes imbriquées et énumérations, et fonctions introduites pour la première fois avec friend déclarations dans les corps de classe;
  • noms de tous les templates non listés ci-dessus (c'est-à-dire pas les templates de fonction déclarés static ).

Tous les noms suivants, d'abord déclarés au niveau d'un bloc, ont une liaison externe :

  • noms des variables déclarées extern ;
  • noms des fonctions.

Liaison de module

Les noms déclarés au niveau de l'espace de noms ont une liaison de module si leurs déclarations sont attachées à un module nommé et ne sont pas exportées, et n'ont pas de liaison interne.

(depuis C++20)

Variables de bloc statiques

Les variables de bloc avec une durée de stockage statique ou thread (depuis C++11) sont initialisées la première fois que le contrôle passe par leur déclaration (sauf si leur initialisation est zéro- ou constante-initialisation , qui peut être effectuée avant que le bloc ne soit entré pour la première fois). Lors de tous les appels ultérieurs, la déclaration est ignorée.

  • Si l'initialisation lève une exception , la variable n'est pas considérée comme initialisée, et l'initialisation sera tentée à nouveau la prochaine fois que le contrôle passera par la déclaration.
  • Si l'initialisation entre récursivement dans le bloc où la variable est en cours d'initialisation, le comportement est indéfini.
  • Si plusieurs threads tentent d'initialiser la même variable locale statique simultanément, l'initialisation se produit exactement une fois (un comportement similaire peut être obtenu pour des fonctions arbitraires avec std::call_once ).
  • Les implémentations usuelles de cette fonctionnalité utilisent des variantes du modèle de verrouillage à double vérification, ce qui réduit la surcharge d'exécution pour les statiques locales déjà initialisées à une simple comparaison booléenne non atomique.
(depuis C++11)

Le destructeur d'une variable de bloc avec une durée de stockage statique est appelé à la fin du programme , mais uniquement si l'initialisation s'est déroulée avec succès.

Les variables avec une durée de stockage statique dans toutes les définitions de la même fonction inline (qui peut être implicitement inline) font toutes référence au même objet défini dans une unité de traduction, tant que la fonction a une liaison externe.

Entités locales à l'unité de traduction

Le concept d'entités locales à l'unité de traduction est normalisé en C++20, consultez cette page pour plus de détails.

Une entité est translation-unit-local (ou TU-local en abrégé) si

  • il a un nom avec une liaison interne, ou
  • il n'a pas de nom avec liaison et est introduit dans la définition d'une entité locale à l'unité de traduction, ou
  • il s'agit d'un modèle ou d'une spécialisation de modèle dont l'argument de modèle ou la déclaration de modèle utilise une entité locale à l'unité de traduction.

Des choses indésirables (généralement une violation de ODR ) peuvent se produire si le type d'une entité non-locale à l'unité de traduction dépend d'une entité locale à l'unité de traduction, ou si une déclaration de , ou un guide de déduction pour, (depuis C++17) une entité non-locale à l'unité de traduction nomme une entité locale à l'unité de traduction en dehors de sa

  • corps de fonction pour une fonction non inline ou un modèle de fonction
  • initialiseur pour une variable ou un modèle de variable
  • déclarations friend dans une définition de classe
  • utilisation de la valeur d'une variable, si la variable est utilisable dans des expressions constantes

De telles utilisations sont interdites dans une unité d'interface de module (en dehors de son fragment privé de module, le cas échéant) ou une partition de module, et sont dépréciées dans tout autre contexte.

Une déclaration apparaissant dans une unité de traduction ne peut pas nommer une entité locale à l'unité de traduction déclarée dans une autre unité de traduction qui n'est pas une unité d'en-tête. Une déclaration instanciée pour un template apparaît au point d'instanciation de la spécialisation.

(depuis C++20)

Notes

Les noms au niveau de la portée de l'espace de noms de plus haut niveau (portée de fichier en C) qui sont const et non extern ont une liaison externe en C, mais une liaison interne en C++.

Depuis C++11, auto n'est plus un spécificateur de classe de stockage ; il est utilisé pour indiquer la déduction de type.

En C, l'adresse d'une variable register ne peut pas être prise, mais en C++, une variable déclarée register est sémantiquement indiscernable d'une variable déclarée sans aucun spécificateur de classe de stockage.

(until C++17)

En C++, contrairement au C, les variables ne peuvent pas être déclarées register .

(since C++17)

Les noms des variables thread_local avec liaison interne ou externe référencées depuis différentes portées peuvent faire référence aux mêmes instances ou à des instances différentes selon que le code s'exécute dans le même thread ou dans des threads différents.

Le mot-clé extern peut également être utilisé pour spécifier une liaison de langage et des déclarations d'instanciation explicite de modèles , mais il ne s'agit pas d'un spécificateur de classe de stockage dans ces cas (sauf lorsqu'une déclaration est directement contenue dans une spécification de liaison de langage, auquel cas la déclaration est traitée comme si elle contenait le spécificateur extern ).

Les spécificateurs de classe de stockage, à l'exception de thread_local , ne sont pas autorisés sur les spécialisations explicites et les instanciations explicites :

template<class T>
struct S
{
    thread_local static int tlm;
};
template<>
thread_local int S<float>::tlm = 0; // "static" n'apparaît pas ici

Une variable template const (pouvant être implicite via constexpr ) avait par défaut une liaison interne, ce qui était incohérent avec les autres entités template. Le rapport de défaut CWG2387 a corrigé cela.

(depuis C++14)
inline agit comme une solution de contournement pour CWG2387 en fournissant une liaison externe par défaut. C'est pourquoi le mot-clé inline a été ajouté à de nombreuses variables templates puis retiré après l'acceptation de CWG2387. Les implémentations de la bibliothèque standard doivent également utiliser inline tant qu'un compilateur pris en charge n'a pas implémenté CWG2387. Voir GCC Bugzilla #109126 et MSVC STL PR #4546 . (depuis C++17)
Macro de test de fonctionnalité Valeur Std Fonctionnalité
__cpp_threadsafe_static_init 200806L (C++11) Initialisation et destruction dynamique avec concurrence

Mots-clés

auto , register , static , extern , thread_local , mutable

Exemple

#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();
}

Sortie possible :

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

Rapports de défauts

Les rapports de défauts modifiant le comportement suivants ont été appliqués rétroactivement aux normes C++ précédemment publiées.

DR Appliqué à Comportement tel que publié Comportement corrigé
CWG 216 C++98 les classes et énumérations sans nom dans la portée de classe ont
une liaison différente de celles dans la portée d'espace de noms
elles ont toutes une liaison
externe dans ces portées
CWG 389 C++98 un nom sans liaison ne devrait pas être
utilisé pour déclarer une entité avec liaison
un type sans liaison ne doit pas être utilisé
comme type d'une variable ou fonction
avec liaison, sauf si la variable
ou fonction a une liaison de langage C
CWG 426 C++98 une entité pouvait être déclarée avec à la fois une liaison interne
et externe dans la même unité de traduction
le programme est mal formé dans ce cas
CWG 527 C++98 la restriction de type introduite par la résolution de CWG
389 était également appliquée aux variables et fonctions qui
ne peuvent pas être nommées en dehors de leur propre unité de traduction
la restriction est levée pour ces
variables et fonctions (c'est-à-dire sans
liaison ou avec liaison interne, ou déclarées
dans des espaces de noms sans nom)
CWG 809 C++98 register avait très peu d'utilité déprécié
CWG 1648 C++11 static était implicite même si
thread_local est combiné avec extern
implicite seulement si aucun autre spécificateur
de classe de stockage n'est présent
CWG 1686 C++98
C++11
le nom d'une variable non statique déclarée dans la portée d'espace
de noms avait une liaison interne seulement si elle est explicitement
déclarée const (C++98) ou constexpr (C++11)
nécessite seulement que le type
soit qualifié const
CWG 2019 C++98 la durée de stockage des membres
référence n'était pas spécifiée
identique à celle de leur objet complet
CWG 2387 C++14 incertain si les modèles de variable qualifiés const
ont une liaison interne par défaut
le qualificateur const n'affecte pas
la liaison des modèles de variable
ou de leurs instances
CWG 2533 C++98 la durée de stockage des objets créés
implicitement n'était pas claire
clarifiée
CWG 2850 C++98 il n'était pas clair quand le stockage pour
les paramètres de fonction est désalloué
clarifié
CWG 2872 C++98 la signification de "peut être référencé" n'était pas claire formulation améliorée
P2788R0 C++20 déclarer une variable qualifiée const dans un espace de noms
lui donnait une liaison interne même dans une unité de module
la liaison interne n'est pas donnée

Références

  • Norme C++23 (ISO/CEI 14882:2024) :
  • 6.7.5 Durée de stockage [basic.stc]
  • Norme C++20 (ISO/CEI 14882:2020) :
  • 6.7.5 Durée de stockage [basic.stc]
  • Norme C++17 (ISO/CEI 14882:2017) :
  • 6.7 Durée de stockage [basic.stc]
  • Norme C++14 (ISO/CEI 14882:2014) :
  • 3.7 Durée de stockage [basic.stc]
  • Norme C++11 (ISO/CEI 14882:2011) :
  • 3.7 Durée de stockage [basic.stc]
  • Norme C++03 (ISO/CEI 14882:2003) :
  • 3.7 Durée de stockage [basic.stc]
  • Norme C++98 (ISO/CEI 14882:1998) :
  • 3.7 Durée de stockage [basic.stc]

Voir aussi

Documentation C pour storage duration