const int forty_two = two_i - four;
Помимо использования константных выражений для инициализации переменных, которые могут использоваться в других константных выражениях, есть ряд случаев, где разрешается применять
• Задание границ массива:
int bounds = 99; │
Ошибка, bounds — не константное
int array[bounds];←┘
выражение
const int bounds2 = 99;│
Правильно, bounds2 — константное
int array2[bounds2]; ←┘
выражение
• Задание значения параметра шаблона, не являющего типом:
template
struct test {}; │
Ошибка, bounds —
│
не константное
test
выражение
test
Правильно, bounds2 —
│
константное выражение
• Задание непосредственно в определении класса инициализатора для переменной-члена класса целочисленного типа со спецификаторами static const
:
class X {
static const int the_answer = forty_two;
};
• Употребление в инициализаторах встроенных типов или агрегатов, применяемых для статической инициализации:
struct my_aggregate {
int a;
int b;
};
static my_aggregate ma1 =│
Статическая
{ forty_two, 123 }; ←┘
инициализация
int dummy = 257; │
Динамическая
static my_aggregate ma2 = {dummy, dummy};←┘
инициализация
Такая статическая инициализация полезна для предотвращения зависимости от порядка инициализации и состояний гонки.
Всё это не ново и было описано еще в стандарте С++ 1998 года. Но в новом стандарте появилось и дополнение в части константных выражений — ключевое слово constexpr
.
Ключевое слово constexpr
применяется главным образом как модификатор функции. Если параметр и возвращаемое функцией значение удовлетворяют определенным условиям, а тело функции достаточно простое, то в ее объявлении можно указать constexpr
и использовать функцию в константных выражениях. Например:
constexpr int square(int x) {
return x*x;
}
int array[square(5)];
В этом случае массив array
будет содержать 25 значений, потому что функция square
объявлена как constexpr
. Конечно, из того, что функцию
int dummy = 4;
(1) Ошибка, dummy — не константное
int array[square(dummy)];←┘
выражение
В этом примере dummy
не является константным выражением (1), поэтому не является таковым и square(dummy)
. Это обычный вызов функции, и, следовательно, для задания границ массива array его использовать нельзя.
А.4.1. constexpr
и определенные пользователем типы
До сих пор мы употребляли в примерах только встроенные типы — такие, как int
. Но в новом стандарте С++ допускаются константные выражения любого типа, удовлетворяющего требованиям, предъявляемым к
• в классе должен существовать тривиальный копирующий конструктор;
• в классе должен существовать тривиальный деструктор;
• все нестатические переменные-члены данного класса и его базовых классов должны иметь тривиальный тип;
• в классе должен существовать либо тривиальный конструктор по умолчанию, либо constexpr
-конструктор, отличный от копирующего конструктора.
О constexpr
-конструкторах мы поговорим чуть ниже. А пока обратимся к классам с тривиальным конструктором по умолчанию. Пример такого класса приведён ниже:
class CX {
private:
int а;
int b;
public:
CX() = default; ←
(1)
CX(int a_, int b_) : ←
(2)
a(a_), b(b_) {}
int get_a() const {
return a;
}
int get_b() const {
return b;
}
int foo() const {
return a + b;
}
};
Здесь мы явно объявили конструктор по умолчанию (1) constexpr
-функцию, которая создает новые экземпляры этого класса:
constexpr CX create_cx() {