Speicherklassen-Spezifizierer
Die Speicherklassen-Spezifizierer sind Teil der decl-specifier-seq der Deklarationssyntax eines Namens. Zusammen mit dem Gültigkeitsbereich des Namens steuern sie zwei unabhängige Eigenschaften des Namens: seine Speicherdauer und seine Bindung.
Inhalt |
[bearbeiten] Speicherdauer
Die Speicherdauer ist die Eigenschaft eines Objekts, die die minimale potenzielle Lebensdauer des den Objekts enthaltenden Speichers bestimmt. Die Speicherdauer wird durch die zur Erzeugung des Objekts verwendete Konstruktion bestimmt und ist eine der folgenden:
- statische Speicherdauer
|
(seit C++11) |
- automatische Speicherdauer
- dynamische Speicherdauer
Statische, Thread-(seit C++11) und automatische Speicherdauern sind mit Objekten verbunden, die durch Deklarationen eingeführt werden, sowie mit temporären Objekten. Die dynamische Speicherdauer ist mit Objekten verbunden, die durch einen new-Ausdruck erzeugt werden, oder mit implizit erzeugten Objekten.
Die Kategorien der Speicherdauer gelten auch für Referenzen.
Die Speicherdauer von Teilobjekten und Referenzmembern ist die ihres vollständigen Objekts.
[bearbeiten] Spezifizierer
Die folgenden Schlüsselwörter sind Speicherklassen-Spezifizierer:
|
(bis C++11) |
|
(bis C++17) |
- static
|
(seit C++11) |
- extern
- mutable
In einer decl-specifier-seq kann höchstens ein Speicherklassen-Spezifizierer vorhanden sein, außer dass thread_local mit static oder extern auftreten kann(seit C++11).
mutable hat keine Auswirkung auf die Speicherdauer. Siehe const/volatile für die Verwendung.
Andere Speicherklassen-Spezifizierer können in der decl-specifier-seq der folgenden Deklarationen vorkommen:
| Spezifizierer | Kann in der decl-specifier-seq vorkommen von | ||||||||
|---|---|---|---|---|---|---|---|---|---|
| Variablendeklarationen | Funktionsdeklarationen | Strukturelle Bindungsdeklarationen (seit C++17) | |||||||
| Nicht-Member | Mitglied | Nicht-Member | Mitglied | ||||||
| Nicht-Parameter | Funktionsparameter | Nicht-statisch | Statisch | Nicht-statisch | Statisch | ||||
| auto | Nur im Block-Gültigkeitsbereich | Ja | Nein | Nein | Nein | Nein | Nein | N/A | |
| register | Nur im Block-Gültigkeitsbereich | Ja | Nein | Nein | Nein | Nein | Nein | N/A | |
| static | Ja | Nein | Deklariert statisch | Nur im Namespace-Gültigkeitsbereich | Deklariert statisch | Ja | |||
| thread_local | Ja | Nein | Nein | Ja | Nein | Nein | Nein | Ja | |
| extern | Ja | Nein | Nein | Nein | Ja | Nein | Nein | Nein | |
Anonyme Unions können auch mit static deklariert werden.
|
register ist ein Hinweis, dass die so deklarierte Variable stark genutzt wird, so dass ihr Wert in einem CPU-Register gespeichert werden kann. Der Hinweis kann ignoriert werden, und in den meisten Implementierungen wird er ignoriert, wenn die Adresse der Variablen genommen wird. Diese Verwendung ist veraltet. |
(bis C++17) |
[bearbeiten] Statische Speicherdauer
Eine Variable, die alle folgenden Bedingungen erfüllt, hat statische Speicherdauer:
- Sie gehört zu einem Namespace-Gültigkeitsbereich oder wird zuerst mit static oder extern deklariert.
|
(seit C++11) |
Der Speicher für diese Entitäten dauert während der gesamten Programmlaufzeit.
Thread-SpeicherdauerAlle mit thread_local deklarierten Variablen haben Thread-Speicherdauer. Der Speicher für diese Entitäten dauert während der gesamten Laufzeit des Threads, in dem sie erzeugt werden. Es gibt ein eigenständiges Objekt oder eine eigenständige Referenz pro Thread, und die Verwendung des deklarierten Namens bezieht sich auf die dem aktuellen Thread zugeordnete Entität. |
(seit C++11) |
[bearbeiten] Automatische Speicherdauer
Die folgenden Variablen haben automatische Speicherdauer:
- Variablen, die zu einem Block-Gültigkeitsbereich gehören und nicht explizit als static, thread_local,(seit C++11) oder extern deklariert sind. Der Speicher für solche Variablen hält bis zum Ende des Blocks an, in dem sie erzeugt werden.
- Variablen, die zu einem Parameter-Gültigkeitsbereich gehören (d. h. Funktionsparameter). Der Speicher für einen Funktionsparameter dauert bis unmittelbar nach seiner Zerstörung.
[bearbeiten] Dynamische Speicherdauer
Objekte, die während der Programmausführung durch die folgenden Methoden erzeugt werden, haben dynamische Speicherdauer:
- new-Ausdrücke. Der Speicher für solche Objekte wird von Allokationsfunktionen allokiert und von Deallokationsfunktionen deallokiert.
- Implizite Erzeugung auf andere Weise. Der Speicher für solche Objekte überlappt mit vorhandenem Speicher.
- Ausnahmeobjekte. Der Speicher für solche Objekte wird auf nicht spezifizierte Weise allokiert und deallokiert.
[bearbeiten] Bindung
Ein Name kann externe Bindung, Modulbindung(seit C++20), interne Bindung oder keine Bindung haben.
- Eine Entität, deren Name externe Bindung hat, kann in einer anderen Übersetzungseinheit neu deklariert werden, und die Neudeklaration kann einem anderen Modul zugeordnet werden(seit C++20).
|
(seit C++20) |
- Eine Entität, deren Name interne Bindung hat, kann in einem anderen Gültigkeitsbereich in derselben Übersetzungseinheit neu deklariert werden.
- Eine Entität, deren Name keine Bindung hat, kann nur im selben Gültigkeitsbereich neu deklariert werden.
Die folgenden Bindungen werden erkannt:
[bearbeiten] Keine Bindung
Namen, die im Block-Gültigkeitsbereich deklariert sind, haben keine Bindung:
- Variablen, die nicht explizit als extern deklariert sind (unabhängig vom Modifikator static);
- Lokale Klassen und ihre Member-Funktionen;
- andere Namen, die im Block-Gültigkeitsbereich deklariert sind, wie Typedefs, Aufzählungen und Aufzählungswerte.
Namen, die nicht mit externer, Modul-(seit C++20) oder interner Bindung spezifiziert sind, haben ebenfalls keine Bindung, unabhängig davon, in welchem Gültigkeitsbereich sie deklariert werden.
[bearbeiten] Interne Bindung
Folgende Namen, die im Namespace-Gültigkeitsbereich deklariert sind, haben interne Bindung:
- Variablen, Variablentemplates(seit C++14), Funktionen oder Funktionstemplates, die als static deklariert sind;
- Nicht-Template-(seit C++14)Variablen vom Typ nicht-volatile const-qualifiziert, es sei denn,
|
(seit C++17) |
|
(seit C++20) |
- sie sind explizit als extern deklariert oder
- sie wurden zuvor deklariert und die vorherige Deklaration hatte keine interne Bindung;
- Datenmember von anonymen Unions.
|
Darüber hinaus haben alle Namen, die in unbenannten Namespaces oder einem Namespace innerhalb eines unbenannten Namespaces deklariert sind, selbst wenn sie explizit als extern deklariert wurden, interne Bindung. |
(seit C++11) |
[bearbeiten] Externe Bindung
Variablen und Funktionen mit externer Bindung haben auch eine Sprachbindung, die es ermöglicht, in verschiedenen Programmiersprachen geschriebene Übersetzungseinheiten zu verknüpfen.
Folgende Namen, die im Namespace-Gültigkeitsbereich deklariert sind, haben externe Bindung, es sei denn, sie sind in einem unbenannten Namespace deklariertoder ihre Deklarationen sind an ein benanntes Modul gebunden und nicht exportiert(seit C++20)
- Variablen und Funktionen, die nicht oben aufgeführt sind (d. h. Funktionen, die nicht als static deklariert sind, nicht-const Variablen, die nicht als static deklariert sind, und alle Variablen, die als extern deklariert sind);
- Aufzählungen;
- Namen von Klassen, ihren Member-Funktionen, statischen Datenmembern (const oder nicht), verschachtelten Klassen und Aufzählungen sowie Funktionen, die zuerst durch friend-Deklarationen innerhalb von Klassenkörpern eingeführt wurden;
- Namen aller Templates, die nicht oben aufgeführt sind (d. h. keine als static deklarierten Funktionstemplates).
Folgende Namen, die zuerst im Block-Gültigkeitsbereich deklariert sind, haben externe Bindung:
- Namen von Variablen, die als extern deklariert sind;
- Namen von Funktionen.
ModulbindungNamen, die im Namespace-Gültigkeitsbereich deklariert sind, haben Modulbindung, wenn ihre Deklarationen an ein benanntes Modul gebunden und nicht exportiert sind und keine interne Bindung haben. |
(seit C++20) |
| Dieser Abschnitt ist unvollständig Grund: Beschreibung des Verhaltens, wenn eine Entität mit unterschiedlichen Bindungen in derselben Übersetzungseinheit deklariert wird (6.6 Absatz 6), Hinweis auf den Unterschied zwischen C++20 (ill-formed) und dem aktuellen Entwurf (well-formed). |
[bearbeiten] Statische Blockvariablen
Blockvariablen mit statischer oder Thread-(seit C++11) Speicherdauer werden beim ersten Durchlauf der Steuerung durch ihre Deklaration initialisiert (es sei denn, ihre Initialisierung ist eine Null- oder Konstantinitialisierung, die vor dem ersten Betreten des Blocks erfolgen kann). Bei allen weiteren Aufrufen wird die Deklaration übersprungen.
- Wenn die Initialisierung eine Ausnahme auslöst, wird die Variable nicht als initialisiert betrachtet, und die Initialisierung wird beim nächsten Durchlauf der Steuerung durch die Deklaration erneut versucht.
- Wenn die Initialisierung rekursiv den Block betritt, in dem die Variable gerade initialisiert wird, ist das Verhalten undefiniert.
|
(seit C++11) |
Der Destruktor für eine Blockvariable mit statischer Speicherdauer wird beim Programmende aufgerufen, aber nur, wenn die Initialisierung erfolgreich war.
Variablen mit statischer Speicherdauer in allen Definitionen derselben Inline-Funktion (die implizit inline sein kann) beziehen sich alle auf dasselbe Objekt, das in einer Übersetzungseinheit definiert ist, solange die Funktion externe Bindung hat.
[bearbeiten] Einheitenlokale Entitäten (Translation Unit Local Entities)
Das Konzept der einheitenlokalen Entitäten ist in C++20 standardisiert, siehe diese Seite für weitere Details.
Eine Entität ist einheitenlokal (kurz: TU-lokal), wenn
- sie einen Namen mit interner Bindung hat, oder
- sie keinen Namen mit Bindung hat und innerhalb der Definition einer TU-lokalen Entität eingeführt wird, oder
- sie ein Template oder eine Template-Spezialisierung ist, deren Template-Argument oder Template-Deklaration eine TU-lokale Entität verwendet.
Schlechte Dinge (normalerweise eine Verletzung von ODR) können passieren, wenn der Typ einer nicht-TU-lokalen Entität von einer TU-lokalen Entität abhängt, oder wenn eine Deklaration vonoder eine Deduktionshilfe für(seit C++17) einer nicht-TU-lokalen Entität eine TU-lokale Entität außerhalb ihres
- Funktionskörpers für eine nicht-inline Funktion oder ein Funktionstemplate
- Initialisierungsausdruck für eine Variable oder ein Variablentemplate
- Friend-Deklarationen in einer Klassendefinition
- Verwendung des Werts einer Variablen, wenn die Variable für Konstante Ausdrücke verwendbar ist
|
Solche Verwendungen sind in einer Modulschnittstelleneinheit (außerhalb ihres privaten Modulfragments, falls vorhanden) oder einer Modulpartition nicht zulässig und in jedem anderen Kontext veraltet. Eine Deklaration, die in einer Übersetzungseinheit auftritt, kann keine TU-lokale Entität benennen, die in einer anderen Übersetzungseinheit deklariert wurde, die keine Header-Einheit ist. Eine Deklaration, die für ein Template instanziiert wird, erscheint an der Stelle der Instanziierung der Spezialisierung. |
(seit C++20) |
[bearbeiten] Anmerkungen
Namen im obersten Namespace-Gültigkeitsbereich (Datei-Gültigkeitsbereich in C), die const sind und nicht extern sind, haben in C externe Bindung, aber in C++ interne Bindung.
Seit C++11 ist auto kein Speicherklassen-Spezifizierer mehr; es wird zur Angabe der Typableitung verwendet.
|
In C kann die Adresse einer register-Variable nicht genommen werden, aber in C++ ist eine als register deklarierte Variable semantisch nicht von einer ohne Speicherklassen-Spezifizierer deklarierten Variable zu unterscheiden. |
(bis C++17) |
|
In C++ können im Gegensatz zu C Variablen nicht als register deklariert werden. |
(seit C++17) |
Namen von thread_local-Variablen mit interner oder externer Bindung, auf die von verschiedenen Gültigkeitsbereichen zugegriffen wird, können sich auf dieselben oder auf unterschiedliche Instanzen beziehen, je nachdem, ob der Code im selben oder in verschiedenen Threads ausgeführt wird.
Das Schlüsselwort extern kann auch zur Angabe der Sprachbindung und zur expliziten Template-Instanziierung verwendet werden, ist aber in diesen Fällen kein Speicherklassen-Spezifizierer (außer wenn eine Deklaration direkt in einer Spezifikation der Sprachbindung enthalten ist, in welchem Fall die Deklaration so behandelt wird, als enthielte sie den extern-Spezifizierer).
Speicherklassen-Spezifizierer, mit Ausnahme von thread_local, sind auf expliziten Spezialisierungen und expliziten Instanziierungen nicht zulässig.
template<class T> struct S { thread_local static int tlm; }; template<> thread_local int S<float>::tlm = 0; // "static" does not appear here
|
Ein const (kann durch constexpr impliziert sein) Variablentemplate hatte standardmäßig eine interne Bindung, was inkonsistent mit anderen Templat-Entitäten war. Der Fehlerbericht CWG2387 hat dies korrigiert. |
(seit C++14) |
inline dient als Workaround für CWG2387, indem es standardmäßig externe Bindung gewährt. Deshalb wurde inline zu vielen Variablentemplates hinzugefügt und dann nach der Annahme von CWG2387 entfernt. Standardbibliotheksimplementierungen müssen ebenfalls inline verwenden, solange ein unterstützter Compiler CWG2387 noch nicht implementiert hat. Siehe GCC Bugzilla #109126 und MSVC STL PR #4546. |
(seit C++17) |
| Feature-Testmakro | Wert | Std | Feature |
|---|---|---|---|
__cpp_threadsafe_static_init |
200806L |
(C++11) | Dynamische Initialisierung und Zerstörung mit Nebenläufigkeit |
[bearbeiten] Schlüsselwörter
auto, register, static, extern, thread_local, mutable
[bearbeiten] Beispiel
#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(); }
Mögliche Ausgabe
Rage counter for a: 2 Rage counter for main: 1 Rage counter for b: 2
[bearbeiten] Fehlerberichte
Die folgenden Verhaltensändernden Fehlerberichte wurden rückwirkend auf zuvor veröffentlichte C++-Standards angewendet.
| DR | angewendet auf | Verhalten wie veröffentlicht | Korrigiertes Verhalten |
|---|---|---|---|
| CWG 216 | C++98 | unbenannte Klasse und Aufzählung im Klassengültigkeitsbereich haben unterschiedliche Bindung als im Namespace-Gültigkeitsbereich |
sie alle haben externe Bindung in diesen Gültigkeitsbereichen |
| CWG 389 | C++98 | ein Name ohne Bindung sollte nicht verwendet werden, um eine Entität mit Bindung zu deklarieren |
ein Typ ohne Bindung darf nicht verwendet werden als Typ einer Variablen oder Funktion mit Bindung, es sei denn, die Variable oder Funktion hat C-Sprachbindung |
| CWG 426 | C++98 | eine Entität könnte mit interner und externer Bindung in derselben Übersetzungseinheit deklariert werden |
Das Programm ist in diesem Fall schlecht geformt. |
| CWG 527 | C++98 | die Typbeschränkung, die durch die Auflösung von CWG 389 eingeführt wurde, wurde auch auf Variablen und Funktionen angewendet, die nicht außerhalb ihrer eigenen Übersetzungseinheiten benannt werden können |
die Beschränkung wird für diese aufgehoben Variablen und Funktionen (d. h. ohne Bindung oder interne Bindung, oder deklariert innerhalb unbenannter Namespaces) |
| CWG 809 | C++98 | register hatte sehr wenig Funktion | deprecated |
| CWG 1648 | C++11 | static wurde impliziert, auch wenn thread_local mit extern kombiniert wird |
impliziert nur, wenn kein anderer Speicher klassen-Spezifizierer vorhanden ist |
| CWG 1686 | C++98 C++11 |
der Name einer nicht-statischen Variablen, deklariert im Namespace Gültigkeitsbereich, hatte interne Bindung nur, wenn sie explizit als const (C++98) oder constexpr (C++11) deklariert wurde |
nur der Typ erforderlich war const-qualifiziert zu sein |
| CWG 2019 | C++98 | die Speicherdauer von Referenz Membern war unbestimmt |
dieselbe wie ihr vollständiges Objekt |
| CWG 2387 | C++14 | unklar, ob const-qualifizierte Variablen- Templates standardmäßig interne Bindung haben |
const-Qualifizierer beeinflusst nicht die Bindung von Variablen Templates oder ihre Instanzen |
| CWG 2533 | C++98 | die Speicherdauer von implizit- erzeugten Objekten war unklar |
wurde klargestellt |
| CWG 2850 | C++98 | war unklar, wann der Speicher für Funktionsparameter freigegeben wird |
wurde klargestellt |
| CWG 2872 | C++98 | die Bedeutung von „kann referenziert werden“ war unklar | verbesserte Formulierung |
| P2788R0 | C++20 | die Deklaration einer const-qualifizierten Variable in einem Namespace gab ihr interne Bindung, selbst in einer Modul-Einheit |
interne Bindung wird nicht vergeben |
[bearbeiten] Referenzen
- C++23 Standard (ISO/IEC 14882:2024)
- 6.7.5 Storage duration [basic.stc]
- C++20 Standard (ISO/IEC 14882:2020)
- 6.7.5 Storage duration [basic.stc]
- C++17 Standard (ISO/IEC 14882:2017)
- 6.7 Storage duration [basic.stc]
- C++14 Standard (ISO/IEC 14882:2014)
- 3.7 Storage duration [basic.stc]
- C++11 Standard (ISO/IEC 14882:2011)
- 3.7 Storage duration [basic.stc]
- C++03-Standard (ISO/IEC 14882:2003)
- 3.7 Storage duration [basic.stc]
- C++98 Standard (ISO/IEC 14882:1998)
- 3.7 Storage duration [basic.stc]
[bearbeiten] Siehe auch
| C-Dokumentation für Speicherdauer
|