Константная инициализация. Ч2
То, каким образом в деталях работает константная иницализация, довольно сложно понять, просто читая стандарт и другие официальные источники. И вот почему.
Вот такой пример они дают:
И говорится, что
Но видимо при проведении константной инициализации компилятор только один раз проходит сверху вниз программы. По факту,
Еще более интересные вещи происходят в ассемблер
Это внутреннее представление этих глобальных переменных. static_class_var прям глобальная, ее могу видеть и другие единицы трансляции. Поэтому она с пометкой .globl. const_var же статическая переменная, а значит ее видно видно только из текущей единицы трансляции.
Проблема в том, что
Дело в том, что здесь замешано одно интересное право компилятора. Ему в определенных случаях разрешено устанавливать начальные значения переменным в compile-time, если он уверен, что их значения не изменится на момент начала старта программы.
Этот пример и пример из прошлого поста кстати показывают, что инициализация глобальных переменных идет не совсем сверху вниз. Для каждого этап инициализации это более менее верно, но под каждый этап попадают разные подмножества переменных. Поэтому могут возникать сайд-эффекты, подобные тем, что были в этом посте.
Accept side affects. Stay cool.
#cpcore #compiler
То, каким образом в деталях работает константная иницализация, довольно сложно понять, просто читая стандарт и другие официальные источники. И вот почему.
Вот такой пример они дают:
struct S
{
static const int static_class_var;
};
static const int const_var = 10 * S::static_class_var;
const int S::static_class_var = 5;
int main()
{
std::cout << &const_var << std::endl; // ODR-use for explicit generation of symbol
std::array<int, S::static_class_var> a1; // OK
// std::array<int, const_var> a2; // ERROR
}
И говорится, что
static_class_var
на момент создания const_var
не имеет инициализатора. А так как компилятор в первую очередь выполняет константную инициализацию, поэтому он не может сейчас установить значение для const_var
. Поэтому в начале инициализируется static_class_var
. И вот уже дальше - const_var
.Но видимо при проведении константной инициализации компилятор только один раз проходит сверху вниз программы. По факту,
static_class_var
- константа, инициализированная константным выражением. И по всем канонам должна сама стать константным выражением. Так и получается, ведь мы можем создать std::array из нее. Но вот из const_var
- не можем. Хотя эта переменная тоже проинициализирована константным выражением. Но так, как ее инициализация происходит после константной инициализации, то этот факт не дает ей шанса стать нормальным constant expression.Еще более интересные вещи происходят в ассемблер
е.
.section __TEXT,__const
.globl __ZN1S16static_class_varE ## @_ZN1S16static_class_varE
.p2align 2, 0x0
__ZN1S16static_class_varE:
.long 5 ## 0x5
.p2align 2, 0x0 ## @_ZL9const_var
__ZL9const_var:
.long 50 ## 0x32
Это внутреннее представление этих глобальных переменных. static_class_var прям глобальная, ее могу видеть и другие единицы трансляции. Поэтому она с пометкой .globl. const_var же статическая переменная, а значит ее видно видно только из текущей единицы трансляции.
Проблема в том, что
const_var
выглядит такой же compile-time константой, как и static_class_var
. Хотя по идее тут должна быть какая-нибудь zero-инициализация + динамическая в рантайме. Но array мы не можем создать с const_var
🗿.Дело в том, что здесь замешано одно интересное право компилятора. Ему в определенных случаях разрешено устанавливать начальные значения переменным в compile-time, если он уверен, что их значения не изменится на момент начала старта программы.
Этот пример и пример из прошлого поста кстати показывают, что инициализация глобальных переменных идет не совсем сверху вниз. Для каждого этап инициализации это более менее верно, но под каждый этап попадают разные подмножества переменных. Поэтому могут возникать сайд-эффекты, подобные тем, что были в этом посте.
Accept side affects. Stay cool.
#cpcore #compiler