constexpr int limit = mf + 1; //
constexpr int sz = size(); //
//
Хоть и нельзя использовать обычную функцию как инициализатор для переменной constexpr
, как будет описано в разделе 6.5.2, новый стандарт позволяет определять функции как constexpr
. Такие функции должны быть достаточно просты, чтобы компилятор мог выполнить их во время компиляции. Функции constexpr
можно использовать в инициализаторе переменной constexpr
.
Как правило, ключевое слово
constexpr
имеет смысл использовать для переменных, которые предполагается использовать как константные выражения.
Поскольку константное выражение обрабатывается во время компиляции, есть пределы для типов, которые можно использовать в объявлении constexpr
. Типы, которые можно использовать в объявлении constexpr
, известны как
Все использованные до сих пор типы — арифметический, ссылка и указатель — это литеральные типы. Наш класс Sales_item
и библиотечный тип string
не относятся к литеральным типам. Следовательно, нельзя определить переменные этих типов как constexpr
. Другие виды литеральных типов рассматриваются в разделах 7.5.6 и 19.3.
Хотя указатели и ссылки можно определить как constexpr
, используемые для их инициализации объекты жестко ограничены. Указатель constexpr
можно инициализировать литералом nullptr
или литералом (т.е. константным выражением) 0. Можно также указать на (или связать с) объект, который остается по фиксированному адресу.
По причинам, рассматриваемым в разделе 6.1.1, определенные в функции переменные обычно не хранятся по фиксированному адресу. Следовательно, нельзя использовать указатель constexpr
для указания на такие переменные. С другой стороны, адрес объекта, определенного вне любой функции, является константным выражением и, таким образом, может использоваться для инициализации указателя constexpr
. Как будет описано в разделе 6.1.1, функции могут определять переменные, существующие на протяжении нескольких вызовов этой функция. Как и объект, определенный вне любой функции, эти специальные локальные объекты также имеют фиксированные адреса. Поэтому и ссылка constexpr
может быть связана с такой переменной, и указатель constexpr
может содержать ее адрес.
constexpr
Важно понимать, что при определении указателя в объявлении constexpr
спецификатор constexpr
относится к указателю, а не к типу, на который указывает указатель.
const int *p = nullptr; //
constexpr int *q = nullptr; //
Несмотря на внешний вид, типы p
и q
весьма различны; p
— указатель на константу, тогда как q
— константный указатель. Различие является следствием того факта, что спецификатор constexpr
налагает на определяемый объект спецификатор const
верхнего уровня (см. раздел 2.4.3).
Как и любой другой константный указатель, указатель constexpr
может указать на константный или неконстантный тип.
constexpr int *np = nullptr; //
//
int j = 0;
constexpr int i = 42; //
//
constexpr const int *p = &i //
//
constexpr int *p1 = &j //
Упражнение 2.32. Допустим ли следующий код? Если нет, то как его исправить?
int null = 0, *p = null;
По мере усложнения программ используемые в них типы также становятся все более сложными. Осложнения в использовании типов возникают по двум причинам. Имена некоторых типов трудно писать по памяти. Написание некоторых их форм утомительно и подвержено ошибкам. Кроме того, формат записи сложного типа способен скрыть его цель или значение. Другой источник осложнений кроется в том, что иногда трудно точно определить необходимый тип. Это может потребовать оглянуться на контекст программы.