«удивительная» константа инициализации из-за порядка определения - PullRequest
13 голосов
/ 02 октября 2011

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

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

Увы, звуковая дорожка отсутствует, так же как и примечания, так что я могу только догадываться, что здесь имеется в виду.* "неожиданно" инициализируется динамически, потому что 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

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

Ответы [ 4 ]

3 голосов
/ 31 декабря 2011

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

энергонезависимым константным объектом с предшествующей инициализацией, инициализированным константным выражением

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

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

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

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

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 голосов
/ 02 октября 2011

Я считаю, что правила, изложенные в 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 вам гарантирована статическая инициализация, и вам не нужно полагаться на необязательное поведение компилятора.

2 голосов
/ 02 октября 2011

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

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

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

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

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


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

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

1 голос
/ 02 октября 2011

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

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.

...