Storage class specifiers
Спецификаторы класса хранения являются частью decl-specifier-seq в синтаксисе объявления имени. Вместе с областью видимости имени они управляют двумя независимыми свойствами имени: его продолжительностью хранения и его связыванием .
Содержание |
Продолжительность хранения
Продолжительность хранения — это свойство объекта , которое определяет минимальный потенциальный срок существования памяти, содержащей объект. Продолжительность хранения определяется конструкцией, используемой для создания объекта, и может быть одной из следующих:
- статическая продолжительность хранения
|
(since C++11) |
- автоматическая продолжительность хранения
- динамическая продолжительность хранения
Статическая , потоковая, (начиная с C++11) и автоматическая продолжительности хранения связаны с объектами, введёнными объявлениями и с временными объектами . Динамическая продолжительность хранения связана с объектами, созданными new выражением или с неявно созданными объектами .
Категории продолжительности хранения применимы и к ссылкам.
Продолжительность хранения подобъектов и членов-ссылок соответствует продолжительности хранения их полного объекта.
Спецификаторы
Следующие ключевые слова являются спецификаторами класса хранения :
|
(до C++11) |
|
(до C++17) |
- static
|
(начиная с 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 :
- Он принадлежит к области видимости пространства имён или объявляется впервые с static или extern .
|
(since C++11) |
Хранение этих сущностей продолжается в течение всего времени выполнения программы.
Продолжительность хранения потокаВсе переменные, объявленные с thread_local , имеют продолжительность хранения потока . Хранение для этих сущностей длится в течение времени существования потока, в котором они созданы. Существует отдельный объект или ссылка для каждого потока, и использование объявленного имени ссылается на сущность, связанную с текущим потоком. |
(since C++11) |
Автоматическая продолжительность хранения
Следующие переменные имеют автоматическую продолжительность хранения :
- Переменные, принадлежащие блочной области видимости и не объявленные явно как static , thread_local , (начиная с C++11) или extern . Память для таких переменных существует до выхода из блока, в котором они созданы.
- Переменные, принадлежащие параметрической области видимости (т.е. параметры функций). Память для параметра функции существует до момента непосредственно после его разрушения .
Динамическая продолжительность хранения
Объекты, созданные следующими методами во время выполнения программы, имеют dynamic storage duration :
- new выражения . Память для таких объектов выделяется функциями выделения памяти и освобождается функциями освобождения памяти .
- Неявное создание другими способами. Память для таких объектов перекрывается с некоторой существующей памятью.
- Объекты исключений . Память для таких объектов выделяется и освобождается неопределённым образом.
Связывание
Имя может иметь внешнюю линковку , модульную линковку (начиная с C++20) , внутреннюю линковку , или отсутствие линковки :
- Сущность, имя которой имеет внешнюю линковку, может быть переобъявлена в другой единице трансляции , и переобъявление может быть прикреплено к другому модулю (начиная с C++20) .
|
(since C++20) |
- Сущность, имя которой имеет внутреннюю линковку, может быть переобъявлена в другой области видимости в той же единице трансляции.
- Сущность, имя которой не имеет линковки, может быть переобъявлена только в той же области видимости.
Распознаются следующие связи:
Отсутствие связывания
Любое из следующих имён, объявленных в области видимости блока, не имеет связывания:
- переменные, которые не объявлены явно extern (независимо от модификатора static );
- локальные классы и их функции-члены;
- другие имена, объявленные в области видимости блока, такие как typedef, перечисления и перечислители.
Имена, не указанные с внешней , модульной, (начиная с C++20) или внутренней линковкой, также не имеют линковки, независимо от того, в какой области они объявлены.
Внутренняя компоновка
Любое из следующих имён, объявленных в области видимости пространства имён, имеет внутреннюю линковку:
- переменные , шаблоны переменных (since C++14) , функции или шаблоны функций, объявленные static ;
- нетемплейтные (since C++14) переменные не-volatile const-квалифицированного типа, если только
|
(since C++17) |
|
(since C++20) |
-
- они явно объявлены extern , или
- они были ранее объявлены, и предыдущее объявление не имело внутренней компоновки;
- элементы данных анонимных объединений .
|
Кроме того, все имена, объявленные в безымянных пространствах имён или в пространстве имён внутри безымянного пространства имён, даже явно объявленные extern , имеют внутреннюю линковку. |
(since C++11) |
Внешняя линковка
Переменные и функции с внешней линковкой также имеют языковую линковку , что позволяет связывать единицы трансляции, написанные на разных языках программирования.
Любое из следующих имён, объявленных в области видимости пространства имён, имеет внешнюю линковку, если только они не объявлены в безымянном пространстве имён или их объявления не прикреплены к именованному модулю и не экспортируются (начиная с C++20) :
- переменные и функции, не перечисленные выше (то есть функции, не объявленные static , неконстантные переменные, не объявленные static , и любые переменные, объявленные extern );
- перечисления;
- имена классов, их функций-членов, статических членов данных (константных или нет), вложенных классов и перечислений, а также функций, впервые объявленных с помощью friend внутри тел классов;
- имена всех шаблонов, не перечисленных выше (то есть не шаблонов функций, объявленных static ).
Любое из следующих имен, впервые объявленных в области видимости блока, имеет внешнюю линковку:
- имена переменных, объявленных extern ;
- имена функций.
Модульная линковкаИмена, объявленные в области видимости пространства имен, имеют модульную линковку, если их объявления прикреплены к именованному модулю и не экспортируются, и не имеют внутренней линковки. |
(since C++20) |
|
Этот раздел не завершён
Причина: добавить описание поведения при объявлении сущности с разными связями в одной единице трансляции (параграф 6.6 пункт 6), отметить разницу между C++20 (некорректная конструкция) и текущим черновиком (корректная конструкция) |
Статические переменные блока
Блочные переменные со статической или потоковой (since C++11) длительностью хранения инициализируются при первом прохождении управления через их объявление (если их инициализация не является нулевой или константной инициализацией , которые могут быть выполнены до первого входа в блок). При всех последующих вызовах объявление пропускается.
- Если инициализация выбрасывает исключение , переменная не считается инициализированной, и инициализация будет предпринята снова при следующем прохождении управления через объявление.
- Если инициализация рекурсивно входит в блок, в котором производится инициализация переменной, поведение не определено.
|
(since C++11) |
Деструктор для блочной переменной со статической продолжительностью хранения вызывается при завершении программы , но только если инициализация прошла успешно.
Переменные со статической продолжительностью хранения во всех определениях одной и той же inline-функции (которая может быть неявно inline) ссылаются на один и тот же объект, определённый в одной единице трансляции, при условии что функция имеет внешнюю линковку.
Сущности, локальные для единицы трансляции
Концепция сущностей, локальных для единицы трансляции, стандартизирована в C++20, подробности смотрите на этой странице .
Сущность называется translation-unit-local (или сокращённо TU-local ), если
- он имеет имя с внутренней линковкой, или
- он не имеет имени с линковкой и введён в определении TU-локальной сущности, или
- он является шаблоном или специализацией шаблона, чей аргумент шаблона или объявление шаблона использует TU-локальную сущность.
Плохие вещи (обычно нарушение ODR ) могут произойти, если тип не-TU-локальной сущности зависит от TU-локальной сущности, или если объявление , или deduction guide для, (since C++17) не-TU-локальной сущности упоминает TU-локальную сущность вне её
- тело функции для не встроенной функции или шаблона функции
- инициализатор для переменной или шаблона переменной
- объявления friend в определении класса
- использование значения переменной, если переменная пригодна для использования в константных выражениях
|
Такие использования запрещены в модульной интерфейсной единице (за пределами её приватного модульного фрагмента, если таковой имеется) или в модульном разделе, и устарели в любом другом контексте. Объявление, которое появляется в одной единице трансляции, не может ссылаться на локальную для единицы трансляции сущность, объявленную в другой единице трансляции, которая не является заголовочной единицей. Объявление, инстанцированное для шаблона , появляется в точке инстанцирования специализации. |
(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
|