非 POD 静态值如何初始化?

Pau*_*nta 5 c++ static

与其他一些语言不同,C++ 允许静态数据为任意类型,而不仅仅是普通的旧数据。简单的旧数据初始化很简单(编译器只是将值写入数据段中的适当地址),但其他更复杂的类型则不然。

非 POD 类型的初始化通常如何在 C++ 中实现?特别是,foo第一次执行该函数时究竟发生了什么?使用什么机制来跟踪是否str已经初始化?

#include <string>
void foo() {
    static std::string str("Hello, Stack Overflow!");
}
Run Code Online (Sandbox Code Playgroud)

Pra*_*ian 5

C++11 要求函数局部static变量的初始化是线程安全的。因此,至少在兼容的编译器中,通常会使用某种同步原语,每次输入函数时都需要检查这些原语。

例如,这是该程序代码的汇编列表

#include <string>
void foo() {
    static std::string str("Hello, Stack Overflow!");
}

int main() {}
Run Code Online (Sandbox Code Playgroud)
.LC0:
    .string "Hello, Stack Overflow!"
foo():
    cmpb    $0, guard variable for foo()::str(%rip)
    je  .L14
    ret
.L14:
    pushq   %rbx
    movl    guard variable for foo()::str, %edi
    subq    $16, %rsp
    call    __cxa_guard_acquire
    testl   %eax, %eax
    jne .L15
.L1:
    addq    $16, %rsp
    popq    %rbx
    ret
.L15:
    leaq    15(%rsp), %rdx
    movl    $.LC0, %esi
    movl    foo()::str, %edi
    call    std::basic_string<char, std::char_traits<char>, std::allocator<char> >::basic_string(char const*, std::allocator<char> const&)
    movl    guard variable for foo()::str, %edi
    call    __cxa_guard_release
    movl    $__dso_handle, %edx
    movl    foo()::str, %esi
    movl    std::basic_string<char, std::char_traits<char>, std::allocator<char> >::~basic_string(), %edi
    call    __cxa_atexit
    jmp .L1
    movq    %rax, %rbx
    movl    guard variable for foo()::str, %edi
    call    __cxa_guard_abort
    movq    %rbx, %rdi
    call    _Unwind_Resume
main:
    xorl    %eax, %eax
    ret
Run Code Online (Sandbox Code Playgroud)

__cxa_guard_acquire__cxa_guard_release等守卫静态变量的初始化。