Storage class specifiers
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
|
(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 :
|
(jusqu'à C++11) |
|
(jusqu'à C++17) |
- static
|
(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 .
|
(since C++11) |
Le stockage de ces entités dure pendant toute la durée du programme.
Durée de stockage des threadsToutes 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 :
- new expressions . La mémoire pour ces objets est allouée par des fonctions d'allocation et libérée par des fonctions de désallocation .
- Création implicite par d'autres moyens. La mémoire pour ces objets chevauche une mémoire existante.
- Objets d'exception . La mémoire pour ces objets est allouée et libérée de manière non spécifiée.
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 externe peut être redéclarée dans une autre unité de traduction , et la redéclaration peut être attachée à un module différent (depuis C++20) .
|
(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
|
(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 ;
- membres de données des unions anonymes .
|
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 moduleLes 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) |
|
Cette section est incomplète
Raison : ajouter la description du comportement lorsqu'une entité est déclarée avec des liaisons différentes dans la même unité de traduction (paragraphe 6.6), noter la différence entre C++20 (mal formé) et le brouillon actuel (bien formé) |
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.
|
(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
|