19

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 );
}
4

2 に答える 2

15

使用例では、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 ...*/ });
    // ...
  }
};

どのメンバー関数が最初に呼び出されるかによって、初期化コードが異なる場合があります (ただし、オブジェクトは 1 回だけ初期化されます)。

そのため、単純なケースでは、ローカルの static が適切に機能します (コンパイラでサポートされている場合) が、call_once.

于 2013-07-01T17:20:29.417 に答える
8

初期化中にスローされた例外が存在する場合でも、両方のコード スニペットの動作は同じです。

この結論は、c++11 標準 (ドラフト n3337) からの次の引用 (の私の解釈) に基づいています。

  • 1 セクション6.7 宣言ステートメント第 4 節は、次のように述べています。

静的ストレージ期間 (3.7.1) またはスレッドストレージ期間 (3.7.2) を持つすべてのブロックスコープ変数のゼロ初期化 (8.5) は、他の初期化が行われる前に実行されます。該当する場合、静的ストレージ期間を持つブロック スコープ エンティティの一定の初期化 (3.6.2) は、そのブロックが最初に入力される前に実行されます。実装は、実装が名前空間スコープ (3.6.2) で静的またはスレッド ストレージ期間を持つ変数を静的に初期化することを許可されているのと同じ条件下で、静的またはスレッド ストレージ期間を持つ他のブロック スコープ変数の早期初期化を実行することが許可されます。そうでなければそのような変数は、コントロールがその宣言を最初に通過するときに初期化されます。そのような変数は、初期化の完了時に初期化されたと見なされます。例外をスローして初期化が終了した場合、初期化は完了していないため、次に制御が宣言に入ったときに再試行されます。変数の初期化中に制御が同時に宣言に入った場合、同時実行は初期化の完了を待つ必要があります。

これは、次のことを意味します。

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

bは 1 回だけ初期化されることが保証されています。つまり、ラムダは (成功して) 1 回だけa = A {...};実行されます。意味は (成功して) 1 回だけ実行されます。

  • 2 セクション30.4.4.2 関数 call-onceは次のように述べています。

関数を呼び出さない call_once の実行は、受動的な実行です。func を呼び出す call_once の実行は、アクティブな実行です。アクティブな実行は、INVOKE (DECAY_COPY ( std::forward(func))、DECAY_COPY (std::forward(args))...) を呼び出す必要があります。このような func の呼び出しで例外がスローされた場合、実行は例外的であり、それ以外の場合は戻ります。例外的な実行は、call_once の呼び出し元に例外を伝播します。任意の once_flag に対する call_once のすべての実行の中で、最大で 1 つが実行を返す必要があります。戻る実行がある場合、それは最後のアクティブな実行になります。戻り実行がある場合にのみ、パッシブ実行があります。

これは、次のことを意味します。

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

へのラムダ引数std::call_onceは (成功して) 1 回だけ実行されます。意味a = A {...};は (成功して) 1 回だけ実行されます。

どちらの場合も、a = A{...};(正常に) 1 回だけ実行されます。

于 2013-07-01T16:04:28.700 に答える