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

Die Speicherklassenspezifizierer sind ein 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 .

Inhaltsverzeichnis

Speicherdauer

Die storage duration ist die Eigenschaft eines Objekts , die die minimale potenzielle Lebensdauer des Speichers definiert, der das Objekt enthält. Die Speicherdauer wird durch das Konstrukt bestimmt, das zur Erstellung des Objekts verwendet wird, und ist eine der folgenden:

  • statische Speicherdauer
  • Thread-Speicherdauer (auch bekannt als Thread-lokale Speicherdauer)
(since C++11)
  • automatische Speicherdauer
  • dynamische Speicherdauer

Static , 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 erstellt werden, oder mit implizit erzeugten Objekten .

Die Speicherdauerkategorien gelten auch für Referenzen.

Die Speicherdauer von Subobjects und Referenzmitgliedern ist die ihres vollständigen Objekts.

Spezifizierer

Die folgenden Schlüsselwörter sind storage class specifiers :

  • auto
(bis C++11)
  • register
(bis C++17)
  • static
  • thread_local
(seit C++11)
  • extern
  • mutable

In einer Dekl-Spezifizierer-Sequenz kann es höchstens einen Speicherklassen-Spezifizierer geben , außer dass thread_local zusammen mit static oder extern (seit C++11) vorkommen kann.

mutable hat keine Auswirkung auf die Speicherdauer. Für die Verwendung siehe const/volatile .

Andere Speicherklassenspezifizierer können in den decl-specifier-seq s der folgenden Deklarationen erscheinen:

Spezifizierer Kann in der decl-specifier-seq von erscheinen
Variablendeklarationen Funktionsdeklarationen Strukturierte Bindungsdeklarationen
(seit C++17)
Nicht-Mitglied Mitglied Nicht-Mitglied Mitglied
Nicht-Parameter Funktionsparameter Nicht-statisch Statisch Nicht-statisch Statisch
auto Nur Blockgültigkeit Ja Nein Nein Nein Nein Nein N/A
register Nur Blockgültigkeit Ja Nein Nein Nein Nein Nein N/A
static Ja Nein Deklariert statisch Nur Namensraumgültigkeit 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 intensiv verwendet wird, sodass ihr Wert in einer CPU-Register gespeichert werden kann. Der Hinweis kann ignoriert werden, und in den meisten Implementierungen wird er ignoriert, wenn die Adresse der Variable genommen wird. Diese Verwendung ist veraltet.

(until C++17)

Statische Speicherdauer

Eine Variable, die alle folgenden Bedingungen erfüllt, hat static storage duration :

  • Es hat keine Thread-Speicherdauer.
(since C++11)

Der Speicher für diese Entitäten besteht für die Dauer des Programms.

Thread-Speicherdauer

Alle Variablen, die mit thread_local deklariert werden, besitzen Thread-Speicherdauer .

Die Speicherdauer dieser Entitäten entspricht der Lebensdauer des Threads, in dem sie erzeugt wurden. Es existiert ein separates Objekt oder Referenz pro Thread, und die Verwendung des deklarierten Namens bezieht sich auf die Entität, die dem aktuellen Thread zugeordnet ist.

(seit C++11)

Automatische Speicherdauer

Die folgenden Variablen haben automatic storage duration :

  • Variablen, die zu einem Blockbereich gehören und nicht explizit als static , thread_local , (since C++11) oder extern deklariert sind. Die Speicherdauer solcher Variablen besteht, bis der Block, in dem sie erzeugt wurden, verlassen wird.
  • Variablen, die zu einem Parameterbereich gehören (d.h. Funktionsparameter). Die Speicherdauer eines Funktionsparameters besteht bis unmittelbar nach seiner Zerstörung .

Dynamische Speicherdauer

Objekte, die durch die folgenden Methoden während der Programmausführung erstellt werden, haben dynamic storage duration :

Verknüpfung

Ein Name kann external linkage  , module linkage (since C++20) , internal linkage , oder no linkage haben:

  • Eine Entität, deren Name Modulverknüpfung aufweist, kann in einer anderen Übersetzungseinheit erneut deklariert werden, sofern die Neudeklaration demselben Modul zugeordnet ist.
(seit C++20)
  • Eine Entität, deren Name interne Verknüpfung hat, kann in einem anderen Gültigkeitsbereich in derselben Übersetzungseinheit erneut deklariert werden.
  • Eine Entität, deren Name keine Verknüpfung hat, kann nur im selben Gültigkeitsbereich erneut deklariert werden.

Die folgenden Verknüpfungen werden erkannt:

Keine Verknüpfung

Jeder der folgenden Namen, die im Blockbereich deklariert werden, hat keine Bindung:

  • Variablen, die nicht explizit deklariert sind extern (unabhängig vom static Modifikator);
  • lokale Klassen und ihre Memberfunktionen;
  • Andere Namen, die im Blockbereich deklariert sind, wie Typdefinitionen, Enumerationen und Enumeratoren.

Nicht mit externer , Modul-, (seit C++20) oder interner Verknüpfung spezifizierte Namen haben ebenfalls keine Verknüpfung, unabhängig davon, in welchem Geltungsbereich sie deklariert sind.

Interne Verknüpfung

Jeder der folgenden Namen, die im Namespace-Bereich deklariert sind, hat interne Verknüpfung:

  • Variablen , Variablentemplates (since C++14) , Funktionen oder Funktionstemplates, die als static deklariert sind;
  • Nicht-Template (since C++14) Variablen von nicht-flüchtigem const-qualifiziertem Typ, es sei denn
  • sie sind inline,
(since C++17)
(since C++20)
  • sie sind explizit als extern deklariert, oder
  • sie wurden zuvor deklariert und die vorherige Deklaration hatte keine interne Verknüpfung;

Zusätzlich haben alle Namen, die in unbenannten Namensräumen oder in einem Namensraum innerhalb eines unbenannten Namensraums deklariert sind, selbst solche, die explizit als extern deklariert wurden, interne Verknüpfung.

(seit C++11)

Externe Verknüpfung

Variablen und Funktionen mit externer Verknüpfung haben ebenfalls Sprachverknüpfung , was das Verknüpfen von Übersetzungseinheiten ermöglicht, die in verschiedenen Programmiersprachen geschrieben sind.

Jeder der folgenden Namen, die im Namespace-Bereich deklariert sind, besitzt externe Verknüpfung, es sei denn, sie sind in einem unbenannten Namespace deklariert oder ihre Deklarationen sind an ein benanntes Modul angehängt 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-konstante Variablen, die nicht als static deklariert sind, und alle Variablen, die als extern deklariert sind);
  • Enumerationen;
  • Namen von Klassen, deren Memberfunktionen, statischen Datenelementen (const oder nicht), geschachtelten Klassen und Enumerationen, sowie Funktionen, die erstmals durch friend -Deklarationen innerhalb von Klassenkörpern eingeführt werden;
  • Namen aller Templates, die nicht oben aufgeführt sind (d.h. keine Funktions-Templates, die als static deklariert sind).

Jeder der folgenden Namen, der zuerst im Blockbereich deklariert wird, hat externe Verknüpfung:

  • Namen von Variablen, die als extern deklariert sind;
  • Namen von Funktionen.

Modulverknüpfung

Namen, die im Namespace-Bereich deklariert werden, haben Modulverknüpfung, wenn ihre Deklarationen an ein benanntes Modul angehängt sind und nicht exportiert werden, und keine interne Verknüpfung haben.

(since C++20)

Statische Blockvariablen

Blockvariablen mit statischer oder Thread- (seit C++11) Speicherdauer werden initialisiert, wenn die Ausführung zum ersten Mal ihre Deklaration durchläuft (sofern ihre Initialisierung nicht Null- oder Konstanteninitialisierung ist, die bereits vor dem ersten Betreten des Blocks durchgeführt werden kann). Bei allen weiteren Aufrufen wird die Deklaration übersprungen.

  • Wenn die Initialisierung eine Exception wirft , wird die Variable nicht als initialisiert betrachtet, und die Initialisierung wird beim nächsten Durchlauf der Deklaration erneut versucht.
  • Wenn die Initialisierung rekursiv in den Block eintritt, in dem die Variable initialisiert wird, ist das Verhalten undefiniert.
  • Wenn mehrere Threads versuchen, dieselbe statische lokale Variable gleichzeitig zu initialisieren, erfolgt die Initialisierung genau einmal (ein ähnliches Verhalten kann für beliebige Funktionen mit std::call_once erzielt werden).
  • Übliche Implementierungen dieser Funktion verwenden Varianten des Double-Checked Locking Patterns, was den Laufzeitaufwand für bereits initialisierte lokale Statics auf einen einzigen nicht-atomaren booleschen Vergleich reduziert.
(seit C++11)

Der Destruktor für eine Blockvariable mit statischer Speicherdauer wird beim Programmende aufgerufen , aber nur wenn die Initialisierung erfolgreich stattgefunden hat.

Variablen mit statischer Speicherdauer in allen Definitionen derselben Inline-Funktion (die implizit inline sein kann) verweisen alle auf dasselbe Objekt, das in einer Übersetzungseinheit definiert ist, sofern die Funktion externe Verknüpfung besitzt.

Übersetzungseinheiten-lokale Entitäten

Das Konzept der übersetzungseinheitslokalen Entitäten wurde in C++20 standardisiert, siehe diese Seite für weitere Details.

Eine Entität ist translation-unit-local (oder kurz TU-local ), wenn

  • es hat einen Namen mit interner Verknüpfung, oder
  • es hat keinen Namen mit Verknüpfung und wird innerhalb der Definition einer TU-lokalen Entität eingeführt, oder
  • es ist eine Template oder Template-Spezialisierung, deren Template-Argument oder Template-Deklaration eine TU-lokale Entität verwendet.

Schlechte Dinge (in der Regel Verletzung der 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 , oder ein deduction guide für, (since C++17) einer nicht-TU-lokalen Entität eine TU-lokale Entität außerhalb ihrer

  • Funktionskörper für eine nicht-inline Funktion oder Funktionsvorlage
  • Initialisierer für eine Variable oder Variablenvorlage
  • Friend-Deklarationen in einer Klassendefinition
  • Verwendung des Werts einer Variable, wenn die Variable in konstanten Ausdrücken 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 erscheint, kann keine TU-lokale Entität benennen, die in einer anderen Übersetzungseinheit deklariert ist, die keine Headereinheit ist. Eine für eine Vorlage instanziierte Deklaration erscheint am Punkt der Instanziierung der Spezialisierung.

(since C++20)

Hinweise

Namen im obersten Namensraumbereich (Dateibereich in C), die const und nicht extern sind, haben externe Bindung in C, aber interne Bindung in C++.

Seit C++11 ist auto kein Speicherklassenspezifizierer mehr; es wird zur Typableitung verwendet.

In C kann die Adresse einer register -Variable nicht genommen werden, aber in C++ ist eine mit register deklarierte Variable semantisch nicht von einer Variable ohne Speicherklassenspezifizierer zu unterscheiden.

(bis C++17)

In C++ können Variablen im Gegensatz zu C nicht mit register deklariert werden.

(seit C++17)

Namen von thread_local Variablen mit interner oder externer Verknüpfung, auf die von verschiedenen Gültigkeitsbereichen aus verwiesen wird, können auf dieselben oder auf verschiedene Instanzen verweisen, abhängig davon, ob der Code im selben oder in verschiedenen Threads ausgeführt wird.

Das extern Schlüsselwort kann auch verwendet werden, um Sprachverknüpfung und explizite Template-Instanziierungsdeklarationen anzugeben, aber es ist in diesen Fällen kein Speicherklassenspezifizierer (außer wenn eine Deklaration direkt in einer Sprachverknüpfungsspezifikation enthalten ist, in welchem Fall die Deklaration so behandelt wird, als ob sie den extern Spezifizierer enthielte).

Speicherklassenspezifizierer, mit Ausnahme von thread_local , sind nicht zulässig bei expliziten Spezialisierungen und expliziten Instanziierungen :

template<class T>
struct S
{
    thread_local static int tlm;
};
template<>
thread_local int S<float>::tlm = 0; // "static" erscheint hier nicht

Eine const (implizit durch constexpr möglich) Variable Template hatte standardmäßig interne Verknüpfung, was inkonsistent mit anderen templatisierten Entitäten war. Defektbericht CWG2387 korrigierte dies.

(since C++14)
inline dient als Workaround für CWG2387 durch standardmäßige externe Verknüpfung. Deshalb wurde inline zu vielen Variable Templates hinzugefügt und nach Annahme von CWG2387 wieder entfernt . Standardbibliothek-Implementierungen müssen ebenfalls inline verwenden, solange ein unterstützter Compiler CWG2387 nicht implementiert hat. Siehe GCC Bugzilla #109126 und MSVC STL PR #4546 . (since C++17)
Feature-Test-Makro Wert Std Feature
__cpp_threadsafe_static_init 200806L (C++11) Dynamische Initialisierung und Destruktion mit Nebenläufigkeit

Schlüsselwörter

auto , register , static , extern , thread_local , mutable

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

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 Klassen und Enumerationen im Klassenbereich haben
andere Verknüpfung als im Namensbereich
sie haben alle externe
Verknüpfung in diesen Bereichen
CWG 389 C++98 ein Name ohne Verknüpfung sollte nicht
verwendet werden, um eine Entität mit Verknüpfung zu deklarieren
ein Typ ohne Verknüpfung darf nicht verwendet
werden als Typ einer Variable oder Funktion
mit Verknüpfung, es sei denn, die Variable
oder Funktion hat C-Sprachverknüpfung
CWG 426 C++98 eine Entität könnte sowohl mit interner
als auch externer Verknüpfung in derselben Übersetzungseinheit deklariert werden
das Programm ist in diesem Fall fehlerhaft
CWG 527 C++98 die durch die Lösung von CWG 389 eingeführte Typbeschränkung
wurde auch auf Variablen und Funktionen angewendet, die
außerhalb ihrer eigenen Übersetzungseinheiten nicht benannt werden können
die Beschränkung wurde für diese
Variablen und Funktionen aufgehoben (d.h. ohne
Verknüpfung oder mit interner Verknüpfung, oder deklariert
innerhalb unbenannter Namensräume)
CWG 809 C++98 register erfüllte kaum eine Funktion veraltet
CWG 1648 C++11 static war impliziert, selbst wenn
thread_local mit extern kombiniert wird
nur impliziert, wenn kein anderer Speicher-
klassenspezifizierer vorhanden ist
CWG 1686 C++98
C++11
der Name einer nicht-statischen Variable, die im Namensbereich
deklariert wurde, hatte nur dann interne Verknüpfung, wenn sie explizit
als const (C++98) oder constexpr (C++11) deklariert wurde
erfordert nur, dass der Typ
const-qualifiziert ist
CWG 2019 C++98 die Speicherdauer von Referenz-
Mitgliedern war nicht spezifiziert
gleich wie ihr vollständiges Objekt
CWG 2387 C++14 unklar, ob const-qualifizierte Variablen-
templates standardmäßig interne Verknüpfung haben
const-Qualifizierer beeinflusst nicht
die Verknüpfung von Variablen-
templates oder ihren Instanzen
CWG 2533 C++98 die Speicherdauer von implizit
erzeugten Objekten war unklar
klargestellt
CWG 2850 C++98 es war unklar, wann der Speicher für
Funktionsparameter freigegeben wird
klargestellt
CWG 2872 C++98 die Bedeutung von "kann referenziert werden" war unklar verbesserte Formulierung
P2788R0 C++20 das Deklarieren einer const-qualifizierten Variable in einem Namensbereich
gab ihr interne Verknüpfung, selbst in einer Moduleinheit
interne Verknüpfung wird nicht vergeben

Referenzen

  • C++23-Standard (ISO/IEC 14882:2024):
  • 6.7.5 Speicherdauer [basic.stc]
  • C++20-Standard (ISO/IEC 14882:2020):
  • 6.7.5 Speicherdauer [basic.stc]
  • C++17-Standard (ISO/IEC 14882:2017):
  • 6.7 Speicherdauer [basic.stc]
  • C++14 Standard (ISO/IEC 14882:2014):
  • 3.7 Speicherdauer [basic.stc]
  • C++11 Standard (ISO/IEC 14882:2011):
  • 3.7 Speicherdauer [basic.stc]
  • C++03 Standard (ISO/IEC 14882:2003):
  • 3.7 Speicherdauer [basic.stc]
  • C++98 Standard (ISO/IEC 14882:1998):
  • 3.7 Speicherdauer [basic.stc]

Siehe auch

C-Dokumentation für storage duration