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-квалификаторов. Например, constconst и volatileconstvolatile не являются допустимыми последовательностями cv-квалификаторов.
[править]Спецификатор mutable
- mutable - разрешает модификацию элемента класса, объявленного mutable, даже если содержащий его объект объявлен константным (т.е. элемент класса является mutable).
Может появляться в объявлении нестатических элементов класса нессылочного неконстантного типа:
class X { mutable constint* 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
- неквалифицированный < constvolatile
- const < constvolatile
- volatile < constvolatile
Ссылки и указатели на 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;// неконстантный объектconstint n2 =0;// константный объектintconst n3 =0;// константный объект (то же что и n2)volatileint n4 =0;// volatile объект conststruct{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 элемент константного объекта не является константой constint& r1 = n1;// ссылка на константу, привязанную к неконстантному объекту// r1 = 2; // ошибка: попытка изменить через ссылку на constconst_cast<int&>(r1)=2;// ok, изменяет неконстантный объект n1 constint& 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 |