cv (const и volatile) квалификаторы типа
Появляются в любом спецификаторе типа, включая последовательность-спецификаторов-объявления грамматики объявления, чтобы указать константность или волатильность объявляемого объекта или именуемого типа.
const- определяет, что тип является константой.volatile- определяет, что тип является volatile.
Содержание |
[править] Объяснение
Для любого типа T (включая неполные типы), кроме типа функции или ссылочного типа, в системе типов C++ есть ещё три различных типа: const-квалифицированный T, volatile-квалифицированный T и const-volatile-квалифицированный T.
- Примечание: считается, что типы массивов имеют ту же cv-квалификацию, что и типы их элементов.
При первом создании объекта используемые cv-квалификаторы (которые могут быть частью последовательности-спецификаторов-объявления или частью декларатора в объявлении или частью идентификатора-типа в выражении new) определяют константность или волатильность объекта следующим образом:
- const объект - объект, чей тип const-квалифицирован, или не mutable подобъект константного объекта. Такой объект нельзя изменить: попытка сделать это напрямую является ошибкой времени компиляции, а попытка сделать это косвенно (например, путём изменения константного объекта через ссылку или указатель на неконстантный тип) приводит к неопределённому поведению.
- volatile объект - объект, чей тип volatile-квалифицирован, или подобъект volatile-объекта, или mutable подобъект const-volatile объекта. Любой доступ (операция чтения или записи, вызов функции-элемента и т.д.), выполненный через выражение glvalue volatile-квалифицированного типа, рассматривается как видимый побочный эффект в целях оптимизации (то есть в рамках одного потока выполнения volatile доступы не могут быть оптимизированы или переупорядочены с другим видимым побочным эффектом, который упорядочивается до или после доступа к volatile. Это делает volatile-объекты подходящими для связи с обработчиком сигнала, но не с другим потоком выполнения, смотрите std::memory_order). Любая попытка сослаться на volatile объект через glvalue не volatile типа (например, через ссылку или указатель на не volatile тип) приводит к неопределённому поведению.
- const volatile объект - объект, чей тип const-volatile-квалифицирован, не mutable подобъект const volatile объекта, const подобъект volatile объекта или не mutable volatile подобъект const объекта. Ведет себя и как 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-квалифицированные типы. В частности, разрешены следующие преобразования:
- ссылка/указатель на неквалифицированный тип могут быть преобразованы в ссылку/указатель на
const - ссылка/указатель на неквалифицированный тип могут быть преобразованы в ссылку/указатель на
volatile - ссылка/указатель на неквалифицированный тип могут быть преобразованы в ссылку/указатель на
const volatile - ссылка/указатель на тип
constмогут быть преобразованы в ссылку/указатель наconst volatile - ссылка/указатель на тип
volatileмогут быть преобразованы в ссылку/указатель наconst volatile
- ссылка/указатель на неквалифицированный тип могут быть преобразованы в ссылку/указатель на
- Примечание: на многоуровневые указатели накладываются дополнительные ограничения.
Чтобы преобразовать ссылку или указатель на cv-квалифицированный тип в ссылку или указатель на менее cv-квалифицированный тип, необходимо использовать const_cast.
[править] Ключевые слова
[править] Примечание
Квалификатор const, используемый при объявлении нелокальной не volatile не шаблонной (начиная с C++14)не inline (начиная с C++17) переменной, которая не объявлена как extern, даёт ей внутреннее связывание. Это отличается от C, где константные переменные области видимости файла имеют внешнее связывание.
Грамматика языка C++ рассматривает mutable как спецификатор-класса-хранения, а не квалификатор типа, но это не влияет на класс хранения или связывание.
|
Некоторые варианты использования volatile устарели:
|
(начиная с C++20) |
[править] Пример
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 const_cast<int&>(r1) = 2; // ok, изменяет неконстантный объект n1 const int& r2 = n2; // ссылка на константу, привязанную к константному объекту // r2 = 2; // ошибка: попытка изменить через ссылку на const // const_cast<int&>(r2) = 2; // поведение неопределено: попытка изменить // константный объект n2 }
Вывод:
# типичный машинный код, созданный на платформе x86_64
# (генерируется только тот код, который способствует наблюдаемым побочным эффектам)
main:
movl $0, -4(%rsp) # volatile int n4 = 0;
movl $3, -4(%rsp) # n4 = 3;
xorl %eax, %eax # возвращает 0 (неявно)
ret[править] Отчёты о дефектах
Следующие изменения поведения были применены с обратной силой к ранее опубликованным стандартам C++:
| Номер | Применён | Поведение в стандарте | Корректное поведение |
|---|---|---|---|
| CWG 1428 | C++98 | определение константного объекта было основано на объявлении | основано на типе объекта |
| CWG 1528 | C++98 | неограниченное количество вхождений каждого cv-квалификатора в одной и той же последовательности cv-квалификаторов |
не более одного раза для каждого cv-квалификатора |
| CWG 1799 | C++98 | mutable может быть применён к элементам данных, не объявленным как const, но типы элементов могут по-прежнему иметь const-квалификацию |
не может применять mutable к элементам данных const-квалифицированных типов |
[править] Смотрите также
| Документация по C для Квалификатор
const | |
| Документация по C для Квалификатор
volatile |

