И все, что предполагает, что код может быть записан через сегмент данных.
ьные статические объекты в C ++ инициализируются один раз, в первый раз, когда они необходимы (что актуально, если инициализация имеет побочный эффект):
void once() {
static bool b = [] {
std::cout << "hello" << std::endl; return true;
} ();
}
once
напечатает «привет» при первом вызове, но не при повторном вызове.
Я положил несколько вариантов этого шаблона вПроводник компилятора и заметил, что все известные реализации (GCC, Clang, ICC, VS) по сути делают одно и то же: скрытую переменнуюguard variable for once()::b
создается и проверяется, нужно ли инициализировать первичную переменную «на этот раз»; если это так, он инициализируется, а затем устанавливается защита, и в следующий раз он не выпрыгнет на код инициализации. например (минимизируется заменой лямбды на вызовextern bool init_b();
):
once():
movzx eax, BYTE PTR guard variable for once()::b[rip]
test al, al
je .L16
ret
.L16:
push rbx
mov edi, OFFSET FLAT:guard variable for once()::b
call __cxa_guard_acquire
test eax, eax
jne .L17
pop rbx
ret
.L17:
call init_b()
pop rbx
mov edi, OFFSET FLAT:guard variable for once()::b
jmp __cxa_guard_release
mov rbx, rax
mov edi, OFFSET FLAT:guard variable for once()::b
call __cxa_guard_abort
mov rdi, rbx
call _Unwind_Resume
... из GCC 6.3 с -O3.
Это не является необоснованным, и я знаю, что на практике условные переходы в любом случае близки к свободным, когда условие является постоянным. Тем не менее, мое внутреннее чувство все равно было бы реализовать это путемООНусловный переход к коду инициализации, который в качестве последнего действия перезаписывает исходный переход с помощьюnop
инструкции. Не обязательно вариант на каждой платформе, но семейство x86 выглядит довольно либерально в отношении того, что вы можете читать или писать и где.
Что такого плохого в этой, по-видимому, простой идее, что ни один основной компилятор не использует ее? (Или мне просто нужно стараться с моими примерами?)