cv (const и volatile) квалификаторы типа
Появляются в любом спецификаторе типа, включая последовательность-спецификаторов-объявления грамматики объявления, чтобы указать константность или волатильность объявляемого объекта или именуемого типа.
- const - определяет, что тип является константой.
- volatile - определяет, что тип является volatile.
Содержание |
[править] Объяснение
Любой тип (возможно неполный), отличный от функционального типа или ссылочного типа это тип из группы из следующих четырёх отдельных, но связанных типов:
- cv-неквалифицированная версия.
- const-квалифицированная версия.
- volatile-квалифицированная версия.
- const-volatile-квалифицированная версия.
Эти четыре типа в одной группе имеют одинаковые требования к представлению и выравниванию.
Считается, что типы массивов имеют ту же cv-квалификацию, что и типы их элементов.
[править] Объекты const и volatile
При первом создании объекта используемые cv-квалификаторы (которые могут быть частью последовательности-спецификаторов-объявления или частью декларатора в объявлении или частью идентификатора-типа в выражении new) определяют константность или волатильность объекта следующим образом:
- Константный объект это
- объект, тип которого является константным, или
- не mutable подобъект константного объекта.
- Такой объект нельзя изменить: попытка сделать это напрямую является ошибкой времени компиляции, а попытка сделать это косвенно (например, путём изменения константного объекта через ссылку или указатель на неконстантный тип) приводит к неопределённому поведению.
- volatile объект это
- объект, тип которого является volatile-квалифицированным,
- подобъект volatile объекта, или
- подобъект mutable const-квалифицированного объекта.
- Каждый доступ (операция чтения или записи, вызов функции-элемента и т.д.) осуществляемый через выражение glvalue volatile-квалифицированного типа, рассматривается как видимый побочный эффект для целей оптимизации (т.е., в пределах одного потока выполнения, volatile доступ не может быть оптимизирован или переупорядочен с другим видимым побочным эффектом, который упорядочен до упорядочен после volatile доступа. Это делает volatile объекты пригодными для связывания с обработчиком сигнала, но не с другим потоком выполнения, смотрите std::memory_order). Любая попытка получить доступ к volatile объекту через glvalue не volatile типа (например, через ссылку или указатель на не volatile тип) приводит к неопределённому поведению.
- Объект const volatile это
- Ведёт себя как константный объект и как volatile объект.
Каждый cv-квалификатор (const и volatile) может появляться не более одного раза в любой последовательности cv-квалификаторов. Например, const const и volatile const volatile не являются допустимыми последовательностями cv-квалификаторов.
[править] Спецификатор mutable
- 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 не шаблонной (начиная с 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 [](...){}(n3, n4, x, r2); // смотрите также: [[maybe_unused]] std::system("g++ -O3 -Wa,-adhln ./main.cpp"); // может выдавать asm в системах POSIX }
Возможный вывод:
# типичный машинный код, созданный на платформе 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
|