Spec-Zone.ru › C++

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 тип) приводит к неопределённому поведению.
  • Постоянный изменяемый объект
  • объект, тип которого является const-volatile-квалифицированным,
  • не-mutable подобъект const volatile объекта,
  • подобъект const изменяемого объекта, или
  • не-mutable 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, mutable

Примечания

Квалификатор const, используемый в объявлении нелокальной, не-volatile не-шаблонной(с C++14)не-встроенной(с C++17) переменной, которая не объявлена extern, присваивает ей внутреннюю привязку. Это отличается от C, где переменные со scope файла const имеют внешнюю привязку.

Грамматика языка C++ обрабатывает mutable как спецификатор класса хранения, а не как квалификатор типа, но это не влияет на класс хранения или привязку.

Некоторые использования volatile устарели:

  • lvalue с volatile типом в качестве операнда встроенных операторов инкремента/декремента;
  • lvalue с volatile типом в качестве левого операнда встроенной прямой присваивания, если выражение прямого присваивания не появляется в контексте невычисления или является выражением с отбрасываемым значением;
  • тип изменяемого объекта в качестве типа параметра функции или возвращаемого значения;
  • квалификатор 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

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API