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

Спецификаторы класса хранения являются частью decl-specifier-seq в синтаксисе объявления имени. Вместе с областью видимости имени они управляют двумя независимыми свойствами имени: его продолжительностью хранения и его связыванием .

Содержание

Продолжительность хранения

Продолжительность хранения — это свойство объекта , которое определяет минимальный потенциальный срок существования памяти, содержащей объект. Продолжительность хранения определяется конструкцией, используемой для создания объекта, и может быть одной из следующих:

  • статическая продолжительность хранения
  • длительность хранения потока (также известная как поточно-локальное хранение)
(since C++11)
  • автоматическая продолжительность хранения
  • динамическая продолжительность хранения

Статическая , потоковая, (начиная с C++11) и автоматическая продолжительности хранения связаны с объектами, введёнными объявлениями и с временными объектами . Динамическая продолжительность хранения связана с объектами, созданными new выражением или с неявно созданными объектами .

Категории продолжительности хранения применимы и к ссылкам.

Продолжительность хранения подобъектов и членов-ссылок соответствует продолжительности хранения их полного объекта.

Спецификаторы

Следующие ключевые слова являются спецификаторами класса хранения :

  • auto
(до C++11)
  • register
(до C++17)
  • static
  • thread_local
(начиная с C++11)
  • extern
  • mutable

В decl-specifier-seq может быть не более одного спецификатора класса хранения , за исключением того, что thread_local может появляться вместе с static или extern (since C++11) .

mutable не влияет на продолжительность хранения. Для его использования см. const/volatile .

Другие спецификаторы класса хранения могут появляться в decl-specifier-seq s следующих объявлений:

Спецификатор Может появляться в decl-specifier-seq s для
Объявления переменных Объявления функций Объявления структурированных привязок
(since C++17)
Не-члены Члены Не-члены Члены
Не-параметры Параметры функций Не-статические Статические Не-статические Статические
auto Только в области видимости блока Да Нет Нет Нет Нет Нет Н/П
register Только в области видимости блока Да Нет Нет Нет Нет Нет Н/П
static Да Нет Объявляет статическим Только в области видимости пространства имен Объявляет статическим Да
thread_local Да Нет Нет Да Нет Нет Нет Да
extern Да Нет Нет Нет Да Нет Нет Нет

Анонимные объединения также могут быть объявлены с static .

register является указанием, что объявленная переменная будет интенсивно использоваться, поэтому её значение может быть сохранено в регистре процессора. Это указание может быть проигнорировано, и в большинстве реализаций оно будет проигнорировано, если берётся адрес переменной. Такое использование устарело.

(до C++17)

Статическая продолжительность хранения

Переменная, удовлетворяющая всем следующим условиям, имеет static storage duration :

  • Он не имеет продолжительности хранения потока.
(since C++11)

Хранение этих сущностей продолжается в течение всего времени выполнения программы.

Продолжительность хранения потока

Все переменные, объявленные с thread_local , имеют продолжительность хранения потока .

Хранение для этих сущностей длится в течение времени существования потока, в котором они созданы. Существует отдельный объект или ссылка для каждого потока, и использование объявленного имени ссылается на сущность, связанную с текущим потоком.

(since C++11)

Автоматическая продолжительность хранения

Следующие переменные имеют автоматическую продолжительность хранения :

  • Переменные, принадлежащие блочной области видимости и не объявленные явно как static , thread_local , (начиная с C++11) или extern . Память для таких переменных существует до выхода из блока, в котором они созданы.
  • Переменные, принадлежащие параметрической области видимости (т.е. параметры функций). Память для параметра функции существует до момента непосредственно после его разрушения .

Динамическая продолжительность хранения

Объекты, созданные следующими методами во время выполнения программы, имеют dynamic storage duration :

Связывание

Имя может иметь внешнюю линковку  , модульную линковку (начиная с C++20) , внутреннюю линковку , или отсутствие линковки :

  • Сущность, имя которой имеет модульную линковку, может быть переобъявлена в другой единице трансляции, при условии что переобъявление присоединено к тому же модулю.
(since C++20)
  • Сущность, имя которой имеет внутреннюю линковку, может быть переобъявлена в другой области видимости в той же единице трансляции.
  • Сущность, имя которой не имеет линковки, может быть переобъявлена только в той же области видимости.

Распознаются следующие связи:

Отсутствие связывания

Любое из следующих имён, объявленных в области видимости блока, не имеет связывания:

  • переменные, которые не объявлены явно extern (независимо от модификатора static );
  • локальные классы и их функции-члены;
  • другие имена, объявленные в области видимости блока, такие как typedef, перечисления и перечислители.

Имена, не указанные с внешней , модульной, (начиная с C++20) или внутренней линковкой, также не имеют линковки, независимо от того, в какой области они объявлены.

Внутренняя компоновка

Любое из следующих имён, объявленных в области видимости пространства имён, имеет внутреннюю линковку:

  • переменные , шаблоны переменных (since C++14) , функции или шаблоны функций, объявленные static ;
  • нетемплейтные (since C++14) переменные не-volatile const-квалифицированного типа, если только
  • они являются inline,
(since C++17)
(since C++20)
  • они явно объявлены extern , или
  • они были ранее объявлены, и предыдущее объявление не имело внутренней компоновки;

Кроме того, все имена, объявленные в безымянных пространствах имён или в пространстве имён внутри безымянного пространства имён, даже явно объявленные extern , имеют внутреннюю линковку.

(since C++11)

Внешняя линковка

Переменные и функции с внешней линковкой также имеют языковую линковку , что позволяет связывать единицы трансляции, написанные на разных языках программирования.

Любое из следующих имён, объявленных в области видимости пространства имён, имеет внешнюю линковку, если только они не объявлены в безымянном пространстве имён или их объявления не прикреплены к именованному модулю и не экспортируются (начиная с C++20) :

  • переменные и функции, не перечисленные выше (то есть функции, не объявленные static , неконстантные переменные, не объявленные static , и любые переменные, объявленные extern );
  • перечисления;
  • имена классов, их функций-членов, статических членов данных (константных или нет), вложенных классов и перечислений, а также функций, впервые объявленных с помощью friend внутри тел классов;
  • имена всех шаблонов, не перечисленных выше (то есть не шаблонов функций, объявленных static ).

Любое из следующих имен, впервые объявленных в области видимости блока, имеет внешнюю линковку:

  • имена переменных, объявленных extern ;
  • имена функций.

Модульная линковка

Имена, объявленные в области видимости пространства имен, имеют модульную линковку, если их объявления прикреплены к именованному модулю и не экспортируются, и не имеют внутренней линковки.

(since C++20)

Статические переменные блока

Блочные переменные со статической или потоковой (since C++11) длительностью хранения инициализируются при первом прохождении управления через их объявление (если их инициализация не является нулевой или константной инициализацией , которые могут быть выполнены до первого входа в блок). При всех последующих вызовах объявление пропускается.

  • Если инициализация выбрасывает исключение , переменная не считается инициализированной, и инициализация будет предпринята снова при следующем прохождении управления через объявление.
  • Если инициализация рекурсивно входит в блок, в котором производится инициализация переменной, поведение не определено.
  • Если несколько потоков пытаются одновременно инициализировать одну и ту же статическую локальную переменную, инициализация происходит ровно один раз (аналогичное поведение может быть получено для произвольных функций с помощью std::call_once ).
  • Обычные реализации этой функции используют варианты шаблона двойной проверки блокировки, что сокращает накладные расходы времени выполнения для уже инициализированных локальных статических переменных до единственного неатомарного булева сравнения.
(since C++11)

Деструктор для блочной переменной со статической продолжительностью хранения вызывается при завершении программы , но только если инициализация прошла успешно.

Переменные со статической продолжительностью хранения во всех определениях одной и той же inline-функции (которая может быть неявно inline) ссылаются на один и тот же объект, определённый в одной единице трансляции, при условии что функция имеет внешнюю линковку.

Сущности, локальные для единицы трансляции

Концепция сущностей, локальных для единицы трансляции, стандартизирована в C++20, подробности смотрите на этой странице .

Сущность называется translation-unit-local (или сокращённо TU-local ), если

  • он имеет имя с внутренней линковкой, или
  • он не имеет имени с линковкой и введён в определении TU-локальной сущности, или
  • он является шаблоном или специализацией шаблона, чей аргумент шаблона или объявление шаблона использует TU-локальную сущность.

Плохие вещи (обычно нарушение ODR ) могут произойти, если тип не-TU-локальной сущности зависит от TU-локальной сущности, или если объявление , или deduction guide для, (since C++17) не-TU-локальной сущности упоминает TU-локальную сущность вне её

Такие использования запрещены в модульной интерфейсной единице (за пределами её приватного модульного фрагмента, если таковой имеется) или в модульном разделе, и устарели в любом другом контексте.

Объявление, которое появляется в одной единице трансляции, не может ссылаться на локальную для единицы трансляции сущность, объявленную в другой единице трансляции, которая не является заголовочной единицей. Объявление, инстанцированное для шаблона , появляется в точке инстанцирования специализации.

(since C++20)

Примечания

Имена на уровне пространства имен верхнего уровня (область видимости файла в C), которые являются const и не extern имеют внешнюю линковку в C, но внутреннюю линковку в C++.

Начиная с C++11, auto больше не является спецификатором класса хранения; он используется для указания выведения типа.

В языке C нельзя получить адрес переменной, объявленной как register , однако в C++ переменная, объявленная с ключевым словом register , семантически неотличима от переменной, объявленной без указания класса хранения.

(до C++17)

В C++, в отличие от C, переменные не могут быть объявлены с ключевым словом register .

(начиная с C++17)

Имена thread_local переменных с внутренней или внешней линковкой, на которые ссылаются из разных областей видимости, могут ссылаться на один и тот же или на разные экземпляры в зависимости от того, выполняется ли код в одном и том же или в разных потоках.

Ключевое слово extern также может использоваться для указания языковой линковки и объявлений явной инстанциации шаблонов , но в этих случаях оно не является спецификатором класса хранения (за исключением случаев, когда объявление непосредственно содержится в спецификации языковой линковки, и в этом случае объявление обрабатывается так, как если бы оно содержало спецификатор extern ).

Спецификаторы класса хранения, за исключением thread_local , не допускаются в явных специализациях и явных инстанцированиях :

template<class T>
struct S
{
    thread_local static int tlm;
};
template<>
thread_local int S<float>::tlm = 0; // здесь "static" не появляется

Шаблон переменной с квалификатором const (который может быть подразумеваемым через constexpr ) ранее имел внутреннюю компоновку по умолчанию, что было несогласованно с другими шаблонными сущностями. Дефектный отчет CWG2387 исправил это.

(since C++14)
inline служит обходным решением для CWG2387 , предоставляя внешнюю компоновку по умолчанию. Именно поэтому спецификатор inline был добавлен ко многим шаблонам переменных, а затем удален после принятия CWG2387. Реализации стандартной библиотеки также должны использовать inline до тех пор, пока поддерживаемый компилятор не реализует CWG2387. См. GCC Bugzilla #109126 и MSVC STL PR #4546 . (since C++17)
Макрос проверки возможности Значение Стандарт Возможность
__cpp_threadsafe_static_init 200806L (C++11) Динамическая инициализация и уничтожение с параллелизмом

Ключевые слова

auto , register , static , extern , thread_local , mutable

Пример

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

Возможный вывод:

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

Отчёты о дефектах

Следующие отчеты об изменениях поведения, влияющие на дефекты, были применены задним числом к ранее опубликованным стандартам C++.

DR Применяется к Поведение в опубликованной версии Корректное поведение
CWG 216 C++98 неименованные классы и перечисления в области видимости класса имеют
другую линковку, чем в области видимости пространства имен
все они имеют внешнюю
линковку в этих областях видимости
CWG 389 C++98 имя без линковки не должно
использоваться для объявления сущности с линковкой
тип без линковки не должен использоваться
в качестве типа переменной или функции
с линковкой, если только переменная
или функция не имеет линковки языка C
CWG 426 C++98 сущность могла быть объявлена с внутренней
и внешней линковкой в одной единице трансляции
программа является некорректной в этом случае
CWG 527 C++98 ограничение типов, введенное решением CWG
389, также применялось к переменным и функциям, которые
не могут быть именованы вне своих единиц трансляции
ограничение снято для этих
переменных и функций (т.е. без
линковки или с внутренней линковкой, или объявленных
в неименованных пространствах имен)
CWG 809 C++98 register имел очень мало функций устарел
CWG 1648 C++11 static подразумевался даже если
thread_local комбинируется с extern
подразумевается только если другие спецификаторы
класса памяти отсутствуют
CWG 1686 C++98
C++11
имя нестатической переменной, объявленной в области видимости пространства
имен, имело внутреннюю линковку только если она явно
объявлена const (C++98) или constexpr (C++11)
требуется только чтобы тип
был const-квалифицирован
CWG 2019 C++98 продолжительность хранения ссылочных
членов была неопределена
такая же как у их полного объекта
CWG 2387 C++14 неясно, имеют ли const-квалифицированные шаблоны
переменных внутреннюю линковку по умолчанию
const квалификатор не влияет
на линковку шаблонов переменных
или их экземпляров
CWG 2533 C++98 продолжительность хранения неявно
созданных объектов была неясна
прояснена
CWG 2850 C++98 было неясно, когда память для
параметров функции освобождается
прояснено
CWG 2872 C++98 значение "может быть упомянуто" было неясным улучшена формулировка
P2788R0 C++20 объявление const-квалифицированной переменной в пространстве имен
давало ей внутреннюю линковку даже в модульной единице
внутренняя линковка не присваивается

Ссылки

  • Стандарт C++23 (ISO/IEC 14882:2024):
  • 6.7.5 Продолжительность хранения [basic.stc]
  • Стандарт C++20 (ISO/IEC 14882:2020):
  • 6.7.5 Продолжительность хранения [basic.stc]
  • Стандарт C++17 (ISO/IEC 14882:2017):
  • 6.7 Продолжительность хранения [basic.stc]
  • Стандарт C++14 (ISO/IEC 14882:2014):
  • 3.7 Продолжительность хранения [basic.stc]
  • Стандарт C++11 (ISO/IEC 14882:2011):
  • 3.7 Продолжительность хранения [basic.stc]
  • Стандарт C++03 (ISO/IEC 14882:2003):
  • 3.7 Продолжительность хранения [basic.stc]
  • Стандарт C++98 (ISO/IEC 14882:1998):
  • 3.7 Продолжительность хранения [basic.stc]

Смотрите также

C documentation для storage duration