X::X() : а() {} ←
всегда а == 0
X::X() : а(42) {} ←
всегда а == 42
X::X() {} ←
(1)
Если инициализация а
при конструировании X
не производится (как в третьем примере (1)), то a
остается неинициализированным для нестатических экземпляров X
и инициализируется нулем для экземпляров X
со статическим временем жизни.
Обычно, если вы вручную напишете хотя бы один конструктор, то компилятор не станет генерировать конструктор по умолчанию. Стало быть, если он вам все-таки нужен, его придётся написать самостоятельно, а тогда это странное свойство инициализации теряется. Однако явно объявив конструктор умалчиваемым, вы можете заставить компилятор сгенерировать конструктор по умолчанию и сохранить это свойство:
X::X() = default;
Это свойство используется в атомарных типах (см. раздел 5.2), в которых конструктор по умолчанию явно объявлен умалчиваемым. У таких типов начальное значение не определено, если только не выполняется одно из следующих условий: (а) задан статический класс памяти (тогда значение инициализируется нулем); (b) для инициализации нулем явно вызван конструктор по умолчанию; (с) вы сами явно указали начальное значение. Отметим, что в атомарных типах конструктор для инициализации значением объявлен как constexpr
(см. раздел А.4), чтобы разрешить статическую инициализацию.
Целые литералы, например 42
, — это константные выражения . Равно как и простые арифметические выражения, например 23*2-4
. Частью константного выражения могут быть также const
-переменные любого целочисленного типа, которые сами инициализированы константным выражением:
const int i = 23;
const int two_i = i * 2;
const int four = 4;
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 is;←┘
выражение
test ia2;←┐
Правильно, 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 его использовать нельзя.
Читать дальше