Константная инициализация. Ч1
Это первый шаг, который пытается выполнить компилятор, когда пробует инициализировать переменную. Для него требуется, чтобы инициализатор был константным выражением. То есть его можно было бы вычислить во время компиляции.
И не путать с обычным const! Позже покажу почему.
Также гарантируется, что эта инициализация происходит до любой другой инициализации статиков. На практике же компиляторы вообще сразу в бинарь помещают предвычисленное значение объекта и во время выполнения с ним уже ничего не нужно делать. Пример:
constexpr double constexpr_var{1.0};
double const_intialized_var1{constexpr_var};
const double const_var{const_intialized_var1};
double const_intialized_var2{3.0};
С переменной
constexpr_var
все хорошо, константа присваивается константному выражению и инициализируется эта переменная первой. Далее устанавливается значение для
const_intialized_var1
. Несмотря на то, что эта переменная не константа, ее инициализатор - константное выражение, а этого достаточно для выполнения константной инициализации. Интересно, что дальше устанавливается значение переменной
const_intialized_var2
, а не
const_var
. Хоть
const_var
и константа, ее инициализатор не является константным выражением! Все потому, что у переменной
const_intialized_var1
нет пометки const(constexpr), а значит, хоть она и проинициализирована константой, сама таковой не является. И
const_var
будет инициализироваться последней уже в рантайме.
Точнее немного не так. Она будет проиниализирована последней, но аж 2 раза! Первый раз -
zero-инициализацией на этапе компиляции, второй раз - динамической в рантайме.
Чтобы не пустословить по чем зря, покажу вырезки из ассембера, которые подкрепляют мои слова. Вот чего нашел:
.section __DATA,__data
.globl _const_intialized_var1 ## @const_intialized_var1
.p2align 3, 0x0
_const_intialized_var1:
.quad 0x3ff0000000000000 ## double 1
.globl _const_intialized_var2 ## @const_intialized_var2
.p2align 3, 0x0
_const_intialized_var2:
.quad 0x4008000000000000 ## double 3
.section __TEXT,__const
.p2align 3, 0x0 ## @_ZL13constexpr_var
__ZL13constexpr_var:
.quad 0x3ff0000000000000 ## double 1
.zerofill __DATA,__bss,__ZL9const_var,8,3 ## @_ZL9const_var
.section __DATA,__mod_init_func,mod_init_funcs
.p2align 3, 0x0
.quad __GLOBAL__sub_I_main.cpp
constexpr_var
инициализируется в текстовой секции. Не смотрите, что эта секция расположена в середине, стандарт гарантирует, что ее инициализация произойдет первой(в ином случае
const_intialized_var1
досталась бы фига).
Дальше мы переходим к data секции, в которой подряд инициализируются
const_intialized_var1
и
_const_intialized_var2
. И после всего этого в секции .zerofill у нас заполняется нулями
const_var
.
И в последнюю очередь, уже в рантайме, динамически ини
циализируется const_var.
.section __TEXT,__StaticInit,regular,pure_instructions
.p2align 4, 0x90 ## -- Begin function __cxx_global_var_init
___cxx_global_var_init: ## @__cxx_global_var_init
.cfi_startproc
## %bb.0:
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset %rbp, -16
movq %rsp, %rbp
.cfi_def_cfa_register %rbp
movsd _const_intialized_var1(%rip), %xmm0 ## xmm0 = mem[0],zero
movsd %xmm0, __ZL9const_var(%rip)
popq %rbp
retq
.cfi_endproc
Это рантаймовая рутина, которая запускается перед вызовом main() и инициализирует
const_var
.
Тут можно довольно простую аналогию провести. Константная инициализаци выполняется для тех объектов, которые можно пометить constexpr, и, не учитывая весь остальной код, компиляция после этого успешно завершится.
Define order of your life. Stay cool.
#cppcore #compiler