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 volatilevolatile<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 |