web-dev-qa-db-ja.com

コンストラクターの初期化子リストでの関数呼び出しは順序付けられていますか?

考えてみましょう:

_int f () {
    static int i = 0;
    return i++;
}

struct Test {
    int a, b;
    Test () : a(f()), b(f()) {}
};

Test t;
_

aでの宣言の順序により、bstructの前に初期化されることを知っています。

また、g(f(), f())でのfへの2つの呼び出しがシーケンスされていないことも知っています。

だから私は_t.a == 0_と_t.b == 1_が保証されているのだろうか?

34
Winestone

だから私は_t.a == 0_と_t.b == 1_が保証されているのだろうか?

これは、クラス宣言でabの前にあり、abの初期化の間にf()を呼び出すものがない限り常に当てはまります。クラスメンバーは、クラスで宣言された順序で初期化されます。 [class.base.init]/11:

非委任コンストラクターでは、初期化は次の順序で進行します。[...]

  • 次に、非静的データメンバーは、クラス定義で宣言された順序で初期化されます(ここでも、mem-initializersの順序に関係なく)。

したがって、abの前にあるため、コンストラクターはaを初期化するときに、最初にf()を呼び出し、次にbを初期化するときに2回目に呼び出します。

[class.base.init]/7のため、メンバー初期化子間にシーケンスポイントがあることもわかっています。

[...]各mem-initializerによって実行される初期化は、完全な式を構成します。 mem-initializer内のすべての式は、初期化を実行する完全な式の一部として評価されます。

各初期化子が完全な式であり、各完全な式がシーケンスされていることを示します:[intro.execution]/14

完全な式に関連付けられたすべての値の計算と副作用は、評価される次の完全な式に関連付けられたすべての値の計算と副作用の前にシーケンスされます。

30
NathanOliver

構造体での宣言の順序により、aがbの前に初期化されることを知っています。

それは本当だ。

その制約の私の解釈は、aが初期化される前に初期化式の評価が完了しない限り、bbの前に初期化できないということです。

非静的メンバーを初期化するために使用される式の評価の順序付けについて説明している標準には何もありません。ただし、C++ 11標準(12.6.2/12)には次の例があります。

Mem-initializerのexpression-listまたはbraced-init-list内の名前は、mem-initializerが指定されているコンストラクターのスコープで評価されます。 [例:

class X {
  int a;
  int b;
  int i;
  int j;
  public:
  const int& r;
  X(int i): r(a), b(i), i(i), j(this->i) { }
};

iが初期化された後、this->iの評価がシーケンスされない限り、これは有効ではありません。

6
R Sahu