g ++-再注文のポイントは何ですか?


150

g ++ -Wallオプションには-Wreorderが含まれます。このオプションの機能を以下に説明します。なぜ誰かが気にするのか私には明らかではありません(特に-Wallでデフォルトでこれをオンにするのに十分です)。

-再注文(C ++のみ)
  コードで指定されたメンバー初期化子の順序が異なる場合に警告する
  それらが実行されなければならない順序と一致します。例えば:

    struct A {
      int i;
      int j;
      A():j(0)、i(1){}
    };

  コンパイラーは、iおよびjのメンバー初期化子を次のように再配置します。
  メンバーの宣言順序を一致させ、それに警告を発します
  効果。この警告は-Wallによって有効になります。

2
ここではいくつかの良い答えが、それは誰にも関心があります場合はさておき簡単:G ++本格的誤りとしてこれを治療するためのフラグがあります:-Werror=reorder
マックスBarraclough

回答:


257

検討してください:

struct A {
    int i;
    int j;
    A() : j(0), i(j) { }
};

現在i、ゼロではなく、不明な値に初期化されています。

または、の初期化にiは、順序が重要な副作用がある場合があります。例えば

A(int n) : j(n++), i(n++) { }

80
これは実際にはドキュメントの例です。
ベンS

3
ありがとう。私たちのタイプのほとんどは単純な初期化子を持つPODタイプなので、これは私には起こりませんでした。あなたの例は、g ++マニュアルの例よりもはるかに優れています。
Peeter Joot、2009

5
@Mikeこれは、コンパイラ(gcc)が初期化されていない変数を0に初期化するためですが、これは依存すべきものではありません。iが初期化されていない変数の未知の値のちょうど副作用0であることは0である
ethanwu10

2
@Yakk順序はmanページでした-> SO回答。この例を明示的に示した2007年のmanページのアーカイブを次に示します。ベンSからの賛成票は、誰かがすでに存在していることを確認することさえせずに何かが存在することを示唆している陽気な例です。web.archive.org/web/20070712184121/http://linux.die.net/man/1/...
KymikoLoco

3
@KymikoLocoそれはまったく間違っています。manページの例は、OPの例です(iはに初期化されています1)。ここで、iはに初期化されj、実際に問題を示しています。
jazzpi 2017

42

問題は、誰かがコンストラクターでメンバー初期化子のリストを見て、それらがこの順序で実行されると考えるかもしれないということです(jが最初に、次にi)。そうではなく、クラスでメンバーが定義された順序で実行されます。

あなたが書いA(): j(0), i(j) {}たとしましょう。誰かがそれを読んで、iが値0で終わると思うかもしれません。それ自体は初期化されていないためにジャンクを含むjで初期化したため、そうではありません。

警告はを書くことを思い出させますA(): i(j), j(0) {}


確かに魚のようなルックス/におい!:)間違いなくコードの臭い:)ポイントに正しい明確な説明をありがとう。:)
ウィル

1
「... A():i(j)、j(0){} ...と書くように通知します」この特定のケースでは、クラスメンバーを並べ替えるように通知することをお勧めします。
2.718

18

他の回答は、警告のオプションを正当化するいくつかの良い例を提供しています。私はいくつかの歴史的背景を提供すると思いました。C ++の作成者であるBjarne Stroustrupは、著書『C ++プログラミング言語(第3版、ページ259)』で次のように説明しています。

メンバーのコンストラクターは、それを含むクラスの独自のコンストラクターの本体が実行される前に呼び出されます。コンストラクターは、初期化子リストに表示される順序ではなく、クラスで宣言された順序で呼び出されます。混乱を避けるために、宣言順にイニシャライザを指定することをお勧めします。メンバーのデストラクターは、構築の逆の順序で呼び出されます。


10

イニシャライザに副作用がある場合、これはあなたを噛む可能性があります。検討してください:

int foo() {
    puts("foo");
    return 1;
}

int bar() {
    puts("bar");
    return 2;
}

struct baz {
    int x, y;
    baz() : y(foo()), x(bar()) {}
};

上記は、「bar」、次に「foo」を出力しますが、直感的には、初期化リストに記述された順序であると想定します。

あるいは、場合xyコンストラクタといくつかのユーザ定義型のもので、そのコンストラクタは、同じ非自明な結果で、副作用を有することができます。

また、あるメンバーの初期化子が別のメンバーを参照するときに、それ自体を明示することもできます。


7

警告が存在するのは、コンストラクタを読んだだけでは、のj前に初期化されているように見えるためですi。これは、次のように一方を使用して他方を初期化する場合に問題になります。

struct A {
  int i;
  int j;
  A(): j (0), i (this->j) { }
};

あなただけのコンストラクタを見てみると、これが見える安全な。しかし実際にjは、初期化iに使用される時点でまだ初期化されていないため、コードは期待どおりに動作しません。したがって警告。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.