cv (const и volatile) квалификаторы типов
Появляются в любом спецификаторе типа, включая decl-specifier-seq из грамматики объявления, для указания постоянства или изменчивости объекта, который объявляется, или типа, который называется.
- const - определяет, что тип является постоянным.
- volatile - определяет, что тип является изменяемым.
Объяснение
Любой (возможно неполный) тип, кроме типа функции или типа ссылки, является типом в группе из четырёх различных, но взаимосвязанных типов:
- cv-неквалифицированная версия.
- const-квалифицированная версия.
- volatile-квалифицированная версия.
- const-volatile-квалифицированная версия.
Эти четыре типа в одной группе имеют одинаковые требования к представлению и выравниванию.
Типы массивов считаются имеющими те же cv-квалификаторы, что и их элементные типы.
Объекты const и volatile
Когда объект создаётся впервые, используемые cv-квалификаторы (которые могут быть частью decl-specifier-seq или частью declarator в объявлении, или частью type-id в выражении new) определяют постоянство или изменчивость объекта следующим образом:
- Постоянный объект
-
- объект, тип которого является const-квалифицированным, или
- не-mutable подобъект постоянного объекта.
- Такой объект не может быть изменён: попытка сделать это напрямую является ошибкой времени компиляции, а попытка сделать это косвенно (например, путём изменения постоянного объекта через ссылку или указатель на непостоянный тип) приводит к неопределённому поведению.
- Изменяемый объект
-
- объект, тип которого является volatile-квалифицированным,
- подобъект изменяемого объекта, или
- mutable подобъект const-volatile объекта.
- Каждый доступ (операция чтения или записи, вызов члена функции и т.д.) через выражение glvalue с volatile-квалифицированным типом обрабатывается как видимый побочный эффект для целей оптимизации (то есть в пределах одного потока выполнения volatile-доступы не могут быть оптимизированы или переупорядочены с другим видимым побочным эффектом, который последовательно предшествует или последовательно следует за доступом volatile. Это делает изменяемые объекты подходящими для связи с обработчиком сигналов, но не с другим потоком выполнения, см.
std::memory_order). Любая попытка получить доступ к volatile-объекту через glvalue не-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; // 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
- неквалифицированный < const volatile
- const < const volatile
- volatile < const volatile
Ссылки и указатели на cv-квалифицированные типы могут быть неявно преобразованы в ссылки и указатели на более cv-квалифицированные типы, см. преобразования квалификаций для подробностей.
Для преобразования ссылки или указателя на cv-квалифицированный тип в ссылку или указатель на менее cv-квалифицированный тип необходимо использовать const_cast.
Ключевые слова
Примечания
Квалификатор const, используемый в объявлении нелокальной, не-volatile не-шаблонной(с C++14)не-встроенной(с C++17) переменной, которая не объявлена extern, присваивает ей внутреннюю привязку. Это отличается от C, где переменные со scope файла 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/w/cpp/language/cv