Подтвердить что ты не робот

"удивительная" постоянная инициализация из-за порядка определения

При чтении слайдов о constexpr введение посвящено "неожиданно динамической инициализации с помощью consts". Пример:

struct S {
    static const int c;
};
const int d = 10 * S::c;
const int S::c = 5;

Увы, звуковая дорожка отсутствует, так же как и заметки, поэтому я могу только догадываться, что здесь подразумевается.

Является ли это правильным, что d "неожиданно" инициализируется динамически, поскольку S::c определяется до d?. Объявление S::c до d, вероятно, не достаточно, компилятор нуждается в полном определении, верно?

Тем не менее, я подозреваю, что в следующем примере d будет инициализирован статически?

struct S {
    static const int c;
};
const int S::c = 5;
const int d = 10 * S::c;  // now _after_ defn of S::c

И чтобы взять торт, в С++ 11, , что должно быть constexpr для полной статической инициализации? S::c, d или оба?

4b9b3361

Ответ 1

В первом примере d не инициализируется константным выражением, потому что S::c не

нелетучий объект const с предыдущей инициализацией, инициализируется постоянным выражением

(см. С++ 11 [expr.const] p2, bullet для преобразований lvalue-to-rvalue), поскольку инициализация S::c не предшествует инициализации d. Поэтому статическая инициализация будет использоваться для S::c (поскольку она инициализируется константным выражением), но для d можно использовать динамическую инициализацию.

Поскольку статическая инициализация предшествует динамической инициализации, d будет инициализироваться на 50 ее динамическим инициализатором. Компилятору разрешено преобразовать динамическую инициализацию d в статическую инициализацию, но если это так, оно должно произвести значение, которое d было бы иметь, если бы каждая переменная, которая могла бы использовать динамическую инициализацию, фактически использовала динамическую инициализацию инициализация. В этом случае d инициализируется на 50 в любом случае. См. С++ 11 [basic.start.init] p2 для получения дополнительной информации об этом.

Невозможно добавить constexpr в первый пример, чтобы гарантировать, что статическая инициализация используется для d; для этого вы должны изменить порядок инициализации. Однако добавление constexpr приведет к диагностике для первого примера, что, по крайней мере, позволит вам гарантировать, что динамическая инициализация not используется (вы получаете статическую инициализацию или ошибку компиляции).

Вы можете обновить второй случай, чтобы убедиться, что статическая инициализация используется следующим образом:

struct S {
    static const int c; // do not use constexpr here
};
constexpr int S::c = 5;
constexpr int d = 10 * S::c;

Неправильно использовать constexpr в объявлении переменной, который не является определением, или использовать его в объявлении переменной, который не содержит инициализатор, поэтому const, а не constexpr в определении struct S. Существует одно исключение из этого правила, которое определяется при определении члена данных static constexpr литерала, не целочисленного типа, с инициализатором, указанным внутри класса:

struct T { int n; };
struct U {
    static constexpr T t = { 4 };
};
constexpr T U::t;

В этом случае constexpr должен использоваться в определении класса, чтобы обеспечить инициализацию, и constexpr должен использоваться в определении статического элемента данных, чтобы разрешить его использование в постоянных выражениях.

Ответ 2

Я считаю, что правила, изложенные в 3.6.2, чтобы определить, когда происходит статическая инициализация, не включают инициализацию для d, что является динамической инициализацией. С другой стороны, S::c действительно статически инициализируется (поскольку 5 является постоянным выражением). Поскольку вся статическая инициализация происходит до динамической инициализации, вы получаете ожидаемый результат.

Чтобы сделать d доступным для статической инициализации, он должен быть инициализирован константным выражением. Это, в свою очередь, заставляет вас написать строку S::c inline:

struct S { static constexpr int c = 5; };

const int d = S::c; // statically initialized

Обратите внимание, что стандарт разрешает замену динамической инициализации статической инициализацией, поэтому переупорядочение двух строк в вашем исходном примере приведет к двум различным типам инициализации. Как указывает TonyK, вы можете использовать array[d] в статическом случае, но не в динамическом случае, так что вы можете проверить, какой из них происходит. С помощью подхода constexpr у вас гарантированно будет статическая инициализация, и вам не придется полагаться на необязательное поведение компилятора.

Ответ 3

Для статической инициализации требуется, грубо говоря, инициализатор константного выражения.

Чтобы быть константным выражением, грубо говоря, переменная должна иметь тип const и иметь предыдущую инициализацию с константным выражением.

В первом примере d инициализатор не является константным выражением, поскольку S::c не является одним (у него нет предшествующей инициализации). Следовательно, d не статически инициализируется.

Во втором примере d инициализатор является константным выражением, и все в порядке.

Я упрощаю дело. В полном формальном стандарте это будет примерно в девять раз дольше.


Что касается спецификатора constexpr, объект не имеет, который будет объявлен constexpr. Это просто дополнительная проверка ошибок. (Это около constexpr объектов, а не constexpr функций).

Вы можете объявить S::c constexpr во втором варианте, если вам нужна дополнительная защита от ошибок (возможно, 5 начнет менять свое значение завтра?) Добавление constexpr к первому варианту не может помочь.

Ответ 4

Вы можете узнать, является ли константа статически или динамически инициализирована, пытаясь объявить массив:

struct S {
    static const int c;
};
const int d = 10 * S::c; // (1)
const int S::c = 5;      // (2)

static char array[d];

Этот код не работает в g ​​++ версии 4.7.0, потому что d динамически инициализируется. И если вы обмениваетесь (1) и (2), он компилируется, потому что теперь d статически инициализируется. Но я не могу найти другой способ его исправить, используя constexpr.