【问题标题】:what is the difference between std::call_once and function-level static initializationstd::call_once 和函数级静态初始化有什么区别
【发布时间】:2013-07-02 01:20:29
【问题描述】:

1) std::call_once

A a;
std::once_flag once;

void f ( ) {
    call_once ( once, [ ] { a = A {....}; } );
}

2) 函数级静态

A a;

void f ( ) {
    static bool b = ( [ ] { a = A {....}; } ( ), true );
}

【问题讨论】:

  • 您的两个示例不等价,call_once 忽略了您传递给它的可调用对象的返回值。您可能希望在两个 lambda 表达式中分配给 a
  • 是的,我忘了更改第一个。固定

标签: multithreading c++11


【解决方案1】:

对于您的示例用法,hmjd 的回答完全说明没有区别(call_once 案例中需要额外的全局 once_flag 对象除外。)但是,call_once 案例更灵活,因为 @ 987654324@ 对象未绑定到单个范围。例如,它可以是一个类成员并被多个函数使用:

class X {
  std::once_flag once;

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

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

现在根据首先调用哪个成员函数,初始化代码可能会有所不同(但对象仍只会被初始化一次。)

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

【讨论】:

    【解决方案2】:

    两个代码 sn-ps 具有相同的行为,即使在初始化期间出现异常时也是如此。

    这个结论是基于(我的解释)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 );
    }
    

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

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

    不调用其函数的 call_once 执行是被动执行。调用其函数的 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 {....}; } );
    

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

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

    【讨论】:

    • 这可能是不必要的迂腐,但我认为标准语言(至少引用的部分 - 可能还有其他部分更清楚)并不一定意味着 lambda 只会执行一次,但仅计算结果将仅分配给变量一次。受到保护以防止并行执行的粒度可能未指定或由实现定义。
    猜你喜欢
    • 2010-09-25
    • 2017-08-27
    • 1970-01-01
    • 1970-01-01
    • 2014-10-08
    • 2011-01-20
    • 1970-01-01
    • 2016-10-05
    • 2013-01-13
    相关资源
    最近更新 更多