C++ 中的局部静态对象在第一次需要时初始化一次(如果初始化有副作用,则这是相关的):
void once() {
static bool b = [] {
std::cout << "hello" << std::endl; return true;
} ();
}
once
第一次调用时会打印“hello”,但再次调用时不会打印“hello”。
我已经将这种模式的一些变体放入编译器资源管理器并注意到所有大牌实现(GCC、Clang、ICC、VS)本质上都做同样的事情:隐藏变量guard variable for once()::b
创建,并检查主变量是否需要“这次”初始化;如果是的话,它会被初始化,然后设置保护,下次它不会跳到初始化代码。例如(通过调用替换 lambda 来最小化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。
这并不是没有道理的,而且我知道,在实践中,当条件一致时,条件跳转无论如何都接近于自由。然而,我的直觉仍然是通过un有条件跳转到初始化代码,作为其最后一个操作,将覆盖原始跳转nop
指示。不一定是每个平台上的选项,但 x86 系列对于可以读取或写入的内容以及在何处似乎相当自由。
这个看似简单的想法有什么问题,以至于没有主流编译器使用它? (或者我只需要更加努力地尝试我的例子?)