std :: call_once和函数级静态初始化之间有什么区别

pal*_*pal 17 multithreading c++11

1)std :: call_once

A a;
std::once_flag once;

void f ( ) {
    call_once ( once, [ ] { a = A {....}; } );
}
Run Code Online (Sandbox Code Playgroud)

2)功能级静态

A a;

void f ( ) {
    static bool b = ( [ ] { a = A {....}; } ( ), true );
}
Run Code Online (Sandbox Code Playgroud)

Jon*_*ely 13

对于您的示例用法,hmjd的答案完全解释了没有区别(除了案例中once_flag需要的额外全局对象call_once.)但是,call_once案例更灵活,因为该once_flag对象不依赖于单个范围.例如,它可以是一个类成员,并由多个函数使用:

class X {
  std::once_flag once;

  void doSomething() {
    std::call_once(once, []{ /* init ...*/ });
    // ...
  }

  void doSomethingElse() {
    std::call_once(once, []{ /*alternative init ...*/ });
    // ...
  }
};
Run Code Online (Sandbox Code Playgroud)

现在,根据首先调用的成员函数,初始化代码可以不同(但对象仍然只能初始化一次.)

因此,对于简单的情况,本地静态可以很好地工作(如果您的编译器支持),但是有一些不太常见的用法可能更容易实现call_once.


hmj*_*mjd 6

即使存在初始化期间抛出的异常,两个代码片段也具有相同的行为.

这个结论是基于(我的解释)c ++ 11标准(草案n3337)中的以下引用:

  • 1第6.7声明声明第4条规定:

具有静态存储持续时间(3.7.1)或线程存储持续时间(3.7.2)的所有块范围变量的零初始化(8.5)在任何其他初始化发生之前执行.具有静态存储持续时间的块范围实体的常量初始化(3.6.2)(如果适用)在首次输入块之前执行.允许实现在静态或线程存储持续时间内执行其他块范围变量的早期初始化,条件是允许实现在命名空间范围内静态初始化具有静态或线程存储持续时间的变量(3.6.2).否则这样第一次控件通过其声明时初始化变量; 这样的变量在初始化完成后被认为是初始化的.如果通过抛出异常退出初始化,则初始化未完成,因此下次控制进入声明时将再次尝试初始化.如果控件在初始化变量时同时进入声明,则并发执行应等待初始化完成.88如果控件在初始化变量时以递归方式重新进入声明,则行为未定义.

这意味着:

void f ( ) {
    static bool b = ( [ ] { a = A {....}; } ( ), true );
}
Run Code Online (Sandbox Code Playgroud)

b保证只被初始化一次,这意味着lambda只执行一次(成功),意味着a = A {...};只执行一次(成功).

  • 2第30.4.4.2函数调用一次状态:

不调用其func的call_once的执行是被动执行.调用其func的call_once的执行是一个活动执行.活动执行应调用INVOKE(DECAY_COPY(std :: forward(func)),DECAY_COPY(std :: forward(args))...).如果对func的这种调用抛出异常,则执行异常,否则返回.异常执行应将异常传播给call_once的调用者.在任何给定的once_flag的call_once的所有执行中:最多一个应该是返回执行; 如果有返回执行,则应该是最后一次执行; 只有在返回执行时才会执行被动执行.

这意味着:

void f ( ) {
    call_once ( once, [ ] { a = A {....}; } );
Run Code Online (Sandbox Code Playgroud)

lambda参数将std::call_once仅执行一次(成功),意味着a = A {...};仅执行一次(成功).

在这两种情况下a = A{...};都只执行一次(成功).

  • 它可能是不必要的迂腐,但我认为标准语言(至少引用的部分 - 可能还有其他部分澄清它更多)并不一定意味着lambda只会执行一次,而只是计算结果将会执行只被分配给变量一次.受到保护以防止并行执行的粒度可能是未指定的或实现定义的. (3认同)