Почему инициализация статической переменной ограничена константными выражениями? - PullRequest
2 голосов
/ 06 декабря 2011

Из "C ++ Primer, 5-е издание", стр. 407:

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

  • Неинициализированная статическая переменная имеет все биты, установленные на 0.

  • Статическая переменная может быть инициализирована только с помощью константного выражения.

В константном выражении могут использоваться литеральные константы, const и enum константы и оператор sizeof. Следующий фрагмент кода иллюстрирует эти моменты:

int x;                        // x set to 0
int y = 49;                   // 49 is a constant expression
int z = 2 * sizeof(int) + 1;  // also a constant expression
int m = 2 * z;                // invalid, z not a constant
int main() {...}

Мой вопрос - ПОЧЕМУ это стандарт? Какова (практическая) причина этого.

Какой вред был бы нанесен нам в противном случае?

Особенно мне трудно найти причину недействительности:

int m = 2 * z; // invalid, z not a constant

, поскольку z само по себе уже известно во время компиляции.

Ответ:

Проще говоря, в соответствии со стандартом не гарантируется, что инструкции перед первым утверждением main будут выполнены по порядку. Однако гарантируется, что все статические хранилища будут инициализироваться нулями перед любой другой инициализацией. Это означает, что в примере, который я привел: int m = 2 * z; - неопределенное поведение, и m может либо вычисляться до 2*0=0, либо может оцениваться как `2 * (2 * sizeof (int) + 1).

Вне «стандарта C ++ - ANSI ISO IEC 14882 2003» 3.6.2 (стр.44):

Объекты со статической продолжительностью хранения (3.7.1) должны быть инициализированы нулями (8.5) перед любой другой инициализацией.

...

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

...

И важный бит:

Как следствие, если инициализация объекта obj1 ссылается на объект obj2 области имен со статической продолжительностью хранения потенциально требует динамической инициализации и определяется позже в та же единица перевода, не указано, является ли значение obj2 используется значение полностью инициализированного объекта obj2 (поскольку объект obj2 был статически инициализировано) или будет значением obj2 просто нулевой initialized.`

Дополнительная информация о проблемах такого рода:

http://www.parashift.com/c++-faq-lite/ctors.html#faq-10.14

Редактировать: поставить в ответ. Pubby's был правильным ответом (и был принят), но я действительно пропустил часть «Как следствие ...». Кроме того, я думаю, что ссылка, которую я добавил, может быть полезной.

Ответы [ 5 ]

4 голосов
/ 06 декабря 2011

z инициализируется до 2 * sizeof(int) + 1, но позже его можно изменить на другое значение.Делая явное требование иметь константное выражение в качестве инициализатора, ясно, какое значение вы получите.Если вы разрешите неконстантные выражения, вы можете получить разные значения в зависимости от порядка инициализации.С требованием const ясно, что вы получаете.

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

Статическая инициализация (технически постоянная инициализация) требует константных выражений, но статические переменные продолжительности хранения не делают.

int x;                        // 0 initialized
int y = 49;                   // statically initialized
int z = 2 * sizeof(int) + 1;  // statically initialized
int m = 2 * z;                // dynamically or statically initialized

3.6.2 Вместе нулевая инициализация и постоянная инициализация называются статической инициализацией; все остальные инициализации динамическая инициализация. Статическая инициализация должна быть выполнена до любой динамической инициализации.

Динамическая инициализация может зависеть от функций и других вещей, которые не могут быть решены во время компиляции. Компилятору разрешено выполнять статическую инициализацию, если он может определить результат.

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

inline double fd() { return 1.0; }
extern double d1;
double d2 = d1; // unspecified:
// may be statically initialized to 0.0 or
// dynamically initialized to 1.0
double d1 = fd(); // may be initialized statically to 1.0
1 голос
/ 06 декабря 2011

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

int i = f();

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

0 голосов
/ 06 декабря 2011

Я думаю, что ваш пример верен.Начиная с 14882: 2011,

При переводе можно вычислять постоянные выражения.

и

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

В вашем примере z и m находятся в одной и той же единице перевода.Поэтому я думаю, что m инициализируется константным выражением.

0 голосов
/ 06 декабря 2011

Единственная причина, по которой я могу придумать это, заключается в том, что переменные static хранятся в сегменте .rdata вашего исполняемого файла, и, поскольку это раздел только для чтения исполняемого файла,что-то там, что меняется, не имеет никакого смысла.

Однако я могу ошибаться.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...