cv
(
const
and
volatile
)
type qualifiers
Появляются в любом спецификаторе типа, включая decl-specifier-seq в грамматике объявлений , для указания константности или изменяемости объявляемого объекта или именуемого типа.
- const - определяет, что тип является константным .
- volatile - определяет, что тип является изменчивым .
Содержание |
Объяснение
Любой (возможно неполный ) тип, кроме function type или reference type , является типом в группе из следующих четырех различных, но связанных типов:
- Версия без cv-квалификаторов .
- Версия с const-квалификатором .
- Версия с volatile-квалификатором .
- Версия с const-volatile-квалификаторами .
Эти четыре типа в одной группе имеют одинаковые представление и требования к выравниванию .
Типы массивов считаются имеющими ту же cv-квалификацию, что и их типы элементов.
Константные и волатильные объекты
Когда объект впервые создаётся, использованные cv-квалификаторы (которые могут быть частью decl-specifier-seq или частью declarator в объявлении , или частью type-id в new-expression ) определяют константность или волатильность объекта следующим образом:
- A const object является
-
- объект, тип которого квалифицирован как const, или
- неизменяемый ( mutable ) подобъект const-объекта.
- Такой объект не может быть изменён: попытка сделать это напрямую приводит к ошибке компиляции, а попытка сделать это косвенно (например, путём изменения const-объекта через ссылку или указатель на не-const тип) приводит к неопределённому поведению.
- Объект volatile является
-
- объект, тип которого квалифицирован как volatile,
- подобъект volatile-объекта, или
- mutable подобъект const-volatile объекта.
- Каждое обращение (операция чтения или записи, вызов функции-члена и т.д.), выполняемое через glvalue-выражение volatile-квалифицированного типа, рассматривается как видимый побочный эффект для целей оптимизации (то есть в рамках одного потока выполнения volatile-доступы не могут быть исключены при оптимизации или переупорядочены с другим видимым побочным эффектом, который sequenced-before или sequenced-after относительно volatile-доступа). Это делает volatile-объекты пригодными для взаимодействия с обработчиком сигналов , но не с другим потоком выполнения (см. std::memory_order ). Любая попытка доступа к volatile-объекту через glvalue не-volatile типа (например, через ссылку или указатель на не-volatile тип) приводит к неопределенному поведению.
- A const volatile object является
- Ведет себя и как const объект, и как volatile объект.
Каждый cv-квалификатор ( const и volatile ) может появляться не более одного раза в любой последовательности cv-квалификаторов. Например, const const и volatile const volatile не являются допустимыми последовательностями cv-квалификаторов.
mutable
спецификатор
- mutable - разрешает модификацию члена класса, объявленного как mutable, даже если содержащий объект объявлен константным (т.е., член класса является изменяемым).
Может появляться в объявлении нестатических членов класса нессылочного неконстантного типа:
class X { mutable const int* p; // OK mutable int* const q; // некорректно mutable int& r; // некорректно };
mutable используется для указания, что член не влияет на внешне видимое состояние класса (как часто применяется для мьютексов, мемоизированных кэшей, ленивых вычислений и инструментирования доступа).
class ThreadsafeCounter { mutable std::mutex m; // Правило "M&M": mutable и mutex всегда вместе int data = 0; public: int get() const { std::lock_guard<std::mutex> lk(m); return data; } void inc() { std::lock_guard<std::mutex> lk(m); ++data; } };
Преобразования
Существует частичное упорядочивание cv-квалификаторов по степени возрастания ограничений. Тип можно назвать более или менее cv-квалифицированным, чем:
- неквалифицированный < const
- неквалифицированный < volatile
- неквалифицированный < const volatile
- const < const volatile
- volatile < const volatile
Ссылки и указатели на cv-квалифицированные типы могут неявно преобразовываться в ссылки и указатели на более cv-квалифицированные типы, подробности смотрите в квалификационных преобразованиях .
Для преобразования ссылки или указателя на тип с cv-квалификаторами в ссылку или указатель на тип с меньшими cv-квалификаторами
необходимо использовать
const_cast
.
Примечания
Квалификатор const , используемый в объявлении нелокальной не-volatile не- template (since C++14) не- inline (since C++17) переменной, которая не объявлена как extern , придает ей внутреннюю линковку . Это отличается от C, где const переменные на уровне файла имеют внешнюю линковку.
Грамматика языка C++ рассматривает mutable как storage-class-specifier , а не как квалификатор типа, но это не влияет на класс хранения или линковку.
|
Некоторые применения volatile устарели:
|
(since C++20) |
Ключевые слова
Пример
#include <cstdlib> int main() { int n1 = 0; // неконстантный объект const int n2 = 0; // константный объект int const n3 = 0; // константный объект (аналогично n2) volatile int n4 = 0; // volatile объект const struct { int n1; mutable int n2; } x = {0, 0}; // константный объект с mutable членом n1 = 1; // OK: изменяемый объект // n2 = 2; // ошибка: неизменяемый объект n4 = 3; // OK: рассматривается как побочный эффект // x.n1 = 4; // ошибка: член константного объекта является константным x.n2 = 4; // OK: mutable член константного объекта не является константным const int& r1 = n1; // ссылка на константу, привязанная к неконстантному объекту // r1 = 2; // ошибка: попытка изменения через ссылку на константу const_cast<int&>(r1) = 2; // OK: изменяет неконстантный объект n1 const int& r2 = n2; // ссылка на константу, привязанная к константному объекту // r2 = 2; // ошибка: попытка изменения через ссылку на константу // const_cast<int&>(r2) = 2; // неопределенное поведение: попытка изменения константного объекта n2 [](...){}(n3, n4, x, r2); // см. также: [[maybe_unused]] std::system("g++ -O3 -Wa,-adhln ./main.cpp"); // может вывести ассемблерный код на POSIX системах }
Возможный вывод:
# типичный машинный код, сгенерированный на платформе x86_64
# (выводится только код, вносящий вклад в наблюдаемые побочные эффекты)
main:
movl $0, -4(%rsp) # volatile int n4 = 0;
movl $3, -4(%rsp) # n4 = 3;
xorl %eax, %eax # return 0 (implicit)
ret
Отчеты о дефектах
Следующие отчеты об изменениях поведения, влияющие на дефекты, были применены задним числом к ранее опубликованным стандартам C++.
| DR | Применяется к | Поведение в опубликованной версии | Корректное поведение |
|---|---|---|---|
| CWG 1428 | C++98 | определение 'const object' основывалось на объявлении | основывается на типе объекта |
| CWG 1528 | C++98 |
не было требований к количеству вхождений
каждого cv-квалификатора в одной последовательности cv-квалификаторов |
не более одного раза для
каждого cv-квалификатора |
| CWG 1799 | C++98 |
mutable
мог применяться к элементам данных, не объявленным
const , но типы элементов всё равно могли быть квалифицированы как const |
нельзя применять
mutable
к элементам
данных типов, квалифицированных как const |
Смотрите также
|
C documentation
для
const
квалификатора
|
|
|
C documentation
для
volatile
квалификатора
|