CV-специфицированные значения
В предыдущей статье мы начали говорить о категориях выражений. Я привел примеры, в которых, на мой взгляд, достаточно легко определить принадлежность к той или иной категории. На их основе компилятор проверяет ограничения, оценивая правомерность написанного кода.
В С++ есть способы наложить дополнительные ограничения на действия над данными. Например, запретить пользователю изменять значения с помощью ключевого слова
Стандарт языка использует термин «cv-специфицированный» для описания типов с квалификаторами
Про
Стоит подумать, для каких категорий выражений такие квалификаторы будут приносить пользу? Ограничить возможность изменять значение или запретить кеширование логично для
Несмотря на то, что переменной
Нельзя сказать, что неизменяемый тип является rvalue. Нет, это просто другое свойство, которое накладывает ограничения на действия над данными. Однако, такие выражения могут быть использованы только как
Это может показаться странным, ведь
В отношении
Априори, в совокупности с
Конечно, неприятный казус может случиться, если мы попытаемся обойти это ограничение — применимвыстрелим себе в ногу снимем ограничение на изменение данных. По сути, это прямое игнорирование ограничений, которые по каким-то причинам вводились в код проекта ранее. И вот желательно их выяснить и обойти иначе, а не использовать такие грязные трюки. Короче, это UB!
Наглядный пример, почему использование этого каста является дурным тоном в программировании на C++: https://compiler-explorer.com/z/qK1z3q89q. В общем, на языке переживших новогодние праздники: «главное не смешивать»
У меня есть офигенная кружка! Обожаю пить из неё кофе, пока пишу эти посты.
#cppcore #memory #algorithm
В предыдущей статье мы начали говорить о категориях выражений. Я привел примеры, в которых, на мой взгляд, достаточно легко определить принадлежность к той или иной категории. На их основе компилятор проверяет ограничения, оценивая правомерность написанного кода.
В С++ есть способы наложить дополнительные ограничения на действия над данными. Например, запретить пользователю изменять значения с помощью ключевого слова
const
. Вероятно, что это как-то должно повлиять на категорию выражения, не так ли?Стандарт языка использует термин «cv-специфицированный» для описания типов с квалификаторами
const
и volatile
. Пример:// Запрещаем изменять значение
const int a = 1;
// Запрещаем кешировать значение в регистрах
volatile int b = 2;
// Комбинация двух предыдущих
const volatile int c = 3;
Про
const
вы, наверняка, уже знаете. Вот о квалификаторе volatile
мы еще не говорили, от нас тут нужна хорошая подводка... В рамках этой темы достаточно знать, что volatile
переменные всегда должны лежать в оперативной памяти (т.н. запрет на кеширование значений; запрет на оптимизацию).Стоит подумать, для каких категорий выражений такие квалификаторы будут приносить пользу? Ограничить возможность изменять значение или запретить кеширование логично для
lvalue
:// Returns const reference
// to access for reading only
const std::string& foo() { return lvalue; }
// Accepts const reference
// to access for reading only
void bar(const std::string &lvalue)
// Spawns read-only value
const int magic = 3;
Несмотря на то, что переменной
magic
нельзя присвоить новое значение, она всё ещё принадлежит категории lvalue
:const int magic = 3;
// lvalue rvalue
magic = 5;
// ~~^~~
// Error: assignment of
// read-only variable 'magic'
Нельзя сказать, что неизменяемый тип является rvalue. Нет, это просто другое свойство, которое накладывает ограничения на действия над данными. Однако, такие выражения могут быть использованы только как
rvalue
. Т.е. могут быть только прочитаны, скопированы. Это позволяет ослабить ограничения в таких ситуациях:const int &d = 2; // Ok
Это может показаться странным, ведь
d
должна ссылаться на какое-то значение в памяти. Да и в остальных случаях это работает иначе:int a = 1; // Ok
int &b = a; // Ok
int &c = 2; // Error!
В отношении
с
все вполне логично и понятно — нельзя сослаться и изменять память, которая не выделена под неё. Почему же всё работает для d
? Тут мы видим, что эти данные запрещено изменять и нет запрета на кеширование. Следовательно, при соблюдении этих ограничений дальше, выражение может быть использовано только как rvalue
, т.е. без перезаписи значений в памяти. Компилятор либо подставит это значение по месту требования, либо создаст вспомогательную локальную копию. В общем случае, ни логика, ни работоспособность приложения не нарушится. Живой примерАприори, в совокупности с
volatile
квалификатором такой трюк не прокатит из-за требований volatile:const volatile int &f = 4; // Error!
Конечно, неприятный казус может случиться, если мы попытаемся обойти это ограничение — применим
const_cast<int&>
, т.е. осознанно Наглядный пример, почему использование этого каста является дурным тоном в программировании на C++: https://compiler-explorer.com/z/qK1z3q89q. В общем, на языке переживших новогодние праздники: «главное не смешивать»
У меня есть офигенная кружка! Обожаю пить из неё кофе, пока пишу эти посты.
#cppcore #memory #algorithm