web-dev-qa-db-ja.com

非静的データメンバーおよびネストされたクラスコンストラクターのクラス内初期化を使用する場合のエラー

次のコードは非常に簡単なものであり、正常にコンパイルされるはずです。

struct A
{
    struct B
    {
        int i = 0;
    };

    B b;

    A(const B& _b = B())
        : b(_b)
    {}
};

このコードをg ++バージョン4.7.2、4.8.1、clang ++ 3.2および3.3でテストしました。 g ++ 4.7.2がこのコードのセグメンテーション違反( http://gcc.gnu.org/bugzilla/show_bug.cgi?id=5777 )であるという事実は別として、他のテストされたコンパイラは、あまり説明しません。

g ++ 4.8.1:

test.cpp: In constructor ‘constexpr A::B::B()’:
test.cpp:3:12: error: constructor required before non-static data member for ‘A::B::i’ has been parsed
     struct B
            ^
test.cpp: At global scope:
test.cpp:11:23: note: synthesized method ‘constexpr A::B::B()’ first required here 
     A(const B& _b = B())
                       ^

clang ++ 3.2および3.3:

test.cpp:11:21: error: defaulted default constructor of 'B' cannot be used by non-static data member initializer which appears before end of class definition
    A(const B& _b = B())
                    ^

このコードをコンパイル可能にすることは可能であり、違いはないはずです。次の2つのオプションがあります。

struct B
{
    int i = 0;
    B(){} // using B()=default; works only for clang++
};

または

struct B
{
    int i;
    B() : i(0) {} // classic c++98 initialization
};

このコードは本当に間違っていますか、それともコンパイラが間違っていますか?

87
etam1024

このコードは本当に間違っていますか、それともコンパイラが間違っていますか?

まあ、どちらも。標準には欠陥があります-B::iの初期化子の解析中にAが完了したと見なされることと、B::B()B::i )は、Aの定義内で使用できます。それは明らかに周期的です。このことを考慮:

struct A {
  struct B {
    int i = (A(), 0);
  };
  A() noexcept(!noexcept(B()));
};

これには矛盾があります:B::B()は暗黙的にnoexceptであり、A()がスローしない場合、およびA()はスローしない場合B::B()notnoexcept。この領域には他にも多くのサイクルと矛盾があります。

これは、コアの問題 136 および 1397 によって追跡されます。特にコア問題1397のこの注意に注意してください。

おそらくこれに対処する最良の方法は、非静的データメンバー初期化子がそのクラスの既定のコンストラクターを使用するために不正な形式にすることです。

これは、この問題を解決するためにClangで実装したルールの特殊なケースです。 Clangのルールは、クラスの非静的データメンバー初期化子が解析される前に、クラスのデフォルトのデフォルトコンストラクターを使用できないことです。したがって、Clangはここで診断を発行します。

    A(const B& _b = B())
                    ^

... Clangはデフォルトの初期化子を解析する前にデフォルトの引数を解析し、このデフォルトの引数はBのデフォルトの初期化子が既に解析されている必要があります(暗黙的にB::B()を定義するため)。

80
Richard Smith

たぶんこれが問題です:

§12.15.デフォルトであり、削除として定義されていないデフォルトコンストラクタは、クラスタイプのオブジェクトを作成するためにODR使用(3.2)されるとき、または最初の宣言後に明示的にデフォルト設定されるときに暗黙的に定義されます

したがって、最初のルックアップ時にデフォルトのコンストラクターが生成されますが、Aが完全に定義されておらず、したがってA内のBが見つからないため、ルックアップは失敗します。

0
fscan