int i = 0;
int *const pi = &i; // нельзя изменить значение pi;
// const верхнего уровня
const int ci = 42; // нельзя изменить ci; const верхнего уровня
const int *p2 = &ci; // нельзя изменить p2; const нижнего уровня
const int *const p3 = p2; // справа const верхнего уровня, слева нет
const int &r = ci; // const в ссылочных типах всегда нижнего уровня
Различие между спецификаторами const
верхнего и нижнего уровней проявляется при копировании объекта. При копировании объекта спецификатор const
верхнего уровня игнорируется.
i = ci; // ok: копирование значения ci; спецификатор const верхнего
// уровня в ci игнорируется
p2 = p3; // ok: указываемые типы совпадают; спецификатор const верхнего
// уровня в p3 игнорируется
Копирование объекта не изменяет копируемый объект. Поэтому несущественно, является ли копируемый или копирующий объект константой.
Спецификатор const
нижнего уровня, напротив, никогда не игнорируется. При копировании объектов у них обоих должны быть одинаковые спецификаторы const
нижнего уровня, или должно быть возможно преобразование между типами этих двух объектов. Как правило, преобразование неконстанты в константу возможно, но не наоборот.
int *p = p3; // ошибка: p3 имеет const нижнего уровня, а p - нет
p2 = p3; // ok: p2 имеет то же const нижнего уровня, что и p3
p2 = &i; // ok: преобразование int* в const int* возможно
int &r = ci; // ошибка: невозможно связать обычную int& с
// объектом const int
const int &r2 = i; // ok: const int& можно связать с обычным int
У указателя p3
есть спецификатор const
нижнего и верхнего уровня. При копировании указателя p3
можно проигнорировать его спецификатор const
верхнего уровня, но не тот факт, что он указывает на константный тип. Следовательно, нельзя использовать указатель p3
для инициализации указателя p
, который указывает на простой (неконстантный) тип int
. С другой стороны, вполне можно присвоить указатель p3
указателю p2
. У обоих указателей тот же тип (спецификатор const
нижнего уровня). Тот факт, что p3
— константный указатель (т.е. у него есть спецификатор const
верхнего уровня), не имеет значения.
Упражнение 2.30. Укажите по каждому из следующих объявлений, имеет ли объявляемый объект спецификатор const
нижнего или верхнего уровня.
const int v2 = 0;
int v1 = v2;
int *p1 = &v1, &r1 = v1;
const int *p2 = &v2, *const p3 = &i, &r2 = v2;
Упражнение 2.31. С учетом объявлений в предыдущем упражнении укажите, допустимы ли следующие присвоения. Объясните, как спецификатор const
верхнего или нижнего уровня применяется в каждом случае.
r1 = v2;
p1 = p2; р2 = p1;
p1 = p3; p2 = p3;
2.4.4. Переменные constexpr
и константные выражения
Константное выражение (constant expression) — это выражение, значение которого не может измениться и вычисляется во время компиляции. Литерал — это константное выражение. Константный объект, инициализируемый константным выражением, также является константным выражением. Вскоре мы увидим, что в языке есть несколько контекстов, требующих константных выражений.
Является ли данный объект (или выражение) константным выражением, зависит от типов и инициализаторов. Например:
const int max_files = 20; // max_files - константное выражение
const int limit = max_files + 1; // limit - константное выражение
int staff_size = 27; // staff_size - неконстантное выражение