cv (const и volatile) квалификаторы типа
Используются в любом спецификаторе типа, включая decl-specifier-seq в грамматике объявлений, чтобы указать константность или volatile-квалификацию объявляемого объекта или именуемого типа.
- const — указывает, что тип является константным.
- volatile — указывает, что тип является volatile.
Пояснение
Любой тип, кроме типа функции и ссылочного типа (возможно, неполный), относится к группе из следующих четырёх различных, но связанных типов:
- Неквалифицированная cv-квалификаторами версия.
- Версия с квалификатором const.
- Версия с квалификатором volatile.
- Версия с квалификаторами const и volatile.
Эти четыре типа в одной группе имеют одинаковые требования к представлению и выравниванию.
Считается, что типы массивов имеют ту же cv-квалификацию, что и их типы элементов.
Объекты const и volatile
При первоначальном создании объекта используемые cv-квалификаторы (которые могут быть частью decl-specifier-seq или частью declarator в объявлении либо частью type-id в выражении new) определяют константность или volatile-квалификацию объекта следующим образом:
- Константный объект — это
- объект с типом, квалифицированным как const, или
- недоступный для изменения подобъект константного объекта.
- Такой объект нельзя изменять: попытка сделать это напрямую приводит к ошибке компиляции, а попытка сделать это косвенно (например, изменить константный объект через ссылку или указатель на неконстантный тип) приводит к неопределённому поведению.
- Объект volatile — это
- объект с типом, квалифицированным как volatile,
- подобъект объекта volatile или
- изменяемый подобъект объекта const volatile.
- Каждое обращение (операция чтения или записи, вызов функции-члена и т. д.), выполняемое через glvalue-выражение с типом, квалифицированным как volatile, считается видимым побочным эффектом для целей оптимизации (то есть в пределах одного потока выполнения обращения к volatile нельзя исключать при оптимизации или переставлять относительно другого видимого побочного эффекта, который упорядочен перед обращением к volatile или после него. Поэтому объекты volatile подходят для взаимодействия с обработчиком сигнала, но не с другим потоком выполнения; см.
std::memory_order). Любая попытка обратиться к объекту volatile через glvalue неквалифицированного типа (например, через ссылку или указатель на неквалифицированный тип) приводит к неопределённому поведению.
- Объект const volatile — это
- объект с типом, квалифицированным как const volatile,
- недоступный для изменения подобъект объекта const volatile,
- подобъект const объекта volatile или
- недоступный для изменения подобъект volatile константного объекта.
- Обладает свойствами как константного объекта, так и объекта volatile.
Каждый cv-квалификатор (const и volatile) может встречаться в последовательности cv-квалификаторов не более одного раза. Например, constconst и volatileconstvolatile не являются допустимыми последовательностями cv-квалификаторов.
Спецификатор mutable
- mutable — разрешает изменять объявленный как mutable член класса, даже если содержащий его объект объявлен как const (то есть член класса является изменяемым).
Может использоваться в объявлении нестатических членов класса не ссылочного и неконстантного типа:
class X
{
mutable const int* p; // OK
mutable int* const q; // ill-formed
mutable int& r; // ill-formed
};
mutable используется, чтобы указать, что член не влияет на внешне наблюдаемое состояние класса (часто применяется для мьютексов, кэшей мемоизации, отложенных вычислений и инструментирования доступа).
class ThreadsafeCounter
{
mutable std::mutex m; // The "M&M rule": mutable and mutex go together
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
- неквалифицированный < constvolatile
- const < constvolatile
- volatile < constvolatile
Ссылки и указатели на cv-квалифицированные типы могут неявно преобразовываться в ссылки и указатели на типы с более строгой cv-квалификацией; подробности см. в разделе преобразования квалификаторов.
Чтобы преобразовать ссылку или указатель на cv-квалифицированный тип в ссылку или указатель на тип с менее строгой cv-квалификацией, необходимо использовать const_cast.
Примечания
Квалификатор const, используемый в объявлении нелокальной переменной, не квалифицированной как volatile, которая не является не-шаблоном(начиная с C++14)не-inline(начиная с C++17) и не объявлена как extern, задаёт ей внутреннее связывание. Это отличается от C, где переменные const с областью видимости файла имеют внешнее связывание.
Грамматика языка C++ рассматривает mutable как спецификатор класса хранения, а не как квалификатор типа, но он не влияет на класс хранения или связывание.
| Некоторые случаи использования volatile объявлены устаревшими:
| (начиная с C++20) |
Ключевые слова
Пример
#include <cstdlib>
int main()
{
int n1 = 0; // non-const object
const int n2 = 0; // const object
int const n3 = 0; // const object (same as n2)
volatile int n4 = 0; // volatile object
const struct
{
int n1;
mutable int n2;
} x = {0, 0}; // const object with mutable member
n1 = 1; // OK: modifiable object
// n2 = 2; // error: non-modifiable object
n4 = 3; // OK: treated as a side-effect
// x.n1 = 4; // error: member of a const object is const
x.n2 = 4; // OK: mutable member of a const object isn't const
const int& r1 = n1; // reference to const bound to non-const object
// r1 = 2; // error: attempt to modify through reference to const
const_cast<int&>(r1) = 2; // OK: modifies non-const object n1
const int& r2 = n2; // reference to const bound to const object
// r2 = 2; // error: attempt to modify through reference to const
// const_cast<int&>(r2) = 2; // undefined behavior: attempt to modify const object n2
[](...){}(n3, n4, x, r2); // see also: [[maybe_unused]]
std::system("g++ -O3 -Wa,-adhln ./main.cpp"); // may issue asm on POSIX systems
}
Возможный вывод:
# typical machine code produced on an x86_64 platform
# (only the code that contributes to observable side-effects is emitted)
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 | определение «константного объекта» основывалось на объявлении | основывается на типе объекта |
| CWG 1528 | C++98 | не было требований к количеству вхождений каждого cv-квалификатора в одной последовательности cv-квалификаторов | не более одного раза для каждого cv-квалификатора |
| CWG 1799 | C++98 |
mutable можно было применять к членам данных, не объявленным const, но типы этих членов всё ещё могли быть квалифицированы как const | нельзя применять mutable к членам данных с типами, квалифицированными как const |
См. также
| Документация по C о const квалификаторе | |
| Документация по C о volatile квалификаторе |
© cppreference.com
Licensed under the Creative Commons Attribution-ShareAlike Unported License v3.0.
https://en.cppreference.com/cpp/language/cv