2013-04-10 9 views
8

私は大規模なプロジェクトに取り組んでいます。このプロジェクトには、コンパイルするコードのセクションが含まれていますが、どのように理解できません。したがって、それは円形である...自体がFirst<Traits>に基づいてtypedefである、Twoに基づいてtypedefあるこのテンプレートコードはなぜコンパイルされますか?

template <typename T> 
struct First { 
    typedef int type;   // (A) 
    typename T::Three order; // (B) 
}; 

template <typename T> struct Second { 
    typedef typename T::type type; 
}; 


template <typename T> struct Third { 
    int val; 
    T two; 
}; 

struct Traits { 
    typedef First<Traits> One; 
    typedef Second<One> Two; 
    typedef Third<Two> Three; 
}; 

int main(int argc, char** argv) { 
    Traits::One x; 
}; 

クラスFirstTraitsと参照Traits::Three、上のテンプレート化されています。私はこの単純な例にそれを蒸留しました。しかし、このコードはgcc4.6とVC10の両方でうまくコンパイルされます。しかし、(A)(B)という2つの行の順序を入れ替えた場合、コードはコンパイルされず、Secondtypedefについて不平を言っています。

なぜこのコードがコンパイルされ、typedefとメンバー変数の順序が重要なのはなぜですか?

+0

このようにツリーで紙に描画してみましょう:各定義のためにノードを作成し、型の各(順方向の)宣言を行い、完全な定義が必要で、宣言だけであり、それは円形ではないことがわかります。 – PlasmaHH

+0

Hi - 無関係ですが、P0704が17に対してDRであるかどうかは分かりますか? –

+0

@Kerrekいいえ、私はそのようなことがどのように決定されるかわからない。 – Barry

答えて

3

言いたいことがいくつかあります。

  • Secondはチェーン「から...インスタンス化」と、「不完全な型」のエラーで終わる長いと

    T badObject; 
    

    を含むように改変されている場合、コードが原因真円度あなたに解除されますあなたの代わりに

    typename T::type object; 
    

    を追加する場合、これは、コンパイラが巧みに完全にをカプセル化する必要はありませんそれを観察していることを語っている期待してではなく、は、T::typeが何であるかを知るだけです。これを説明するために、Tは一切現在ビーイング定義オブジェクトが含まれていないので、あなたが合法的に

    First { ... typedef T type; ... } 
    Second { typename T::type object; } 
    

    を持つことができます注意してください、または

    First { ... typedef typename T::One type; ... } 
    Second { typedef typename T::type object; } 
    

    Secondtypedefので、任意のオブジェクトのインスタンスを必要としません。どちらか - しかし、ない、言う、

    First { ... typedef typename T::One type; ... } 
    Second { typename T::type object; } 
    
    コンパイラが実際にFirst<Traits>オブジェクト内にFirst<Traits>オブジェクトをネストする必要があるからです。 (A)スワッピングと(B)との

  • 問題

  • は、コンパイラが上記引っ張っ巧妙なトリックは時間でそれを 1行を解析し、各専門のテンプレートの定義の新しいコピーを導入することで動作していることです。Secondで知る必要がある場合は、Firstの型定義まで達していなければエラーが発生します。
+1

ありがとう、これは完璧です。私はコンパイラが一度に1行になることを理解していませんでした。 – Barry

+0

私は仕様をチェックしていませんが、これが唯一の可能性のある問題であると想定しています。あなたがそれについて考えるなら、あなたはすでに物事が行ごとに起こっていることを知っていました - そのため、 'class A {};クラスB:A;およびクラスB:A; class A {}; 'は異なる! – Sharkos

1

typedefの完全なタイプは必要ありません。

+0

しかしこれはコンパイルされません: 'template struct Foo { typedef typename Derived :: type type; }; 構造体バー:public Foo { typedef int型; }; int main(int argc、char ** argv){ Bar b; } ' – Barry

+0

@Barry:違いは、' Bar'は 'Foo '(継承のため)の定義を必要とし、 'Foo'の' typedef'は 'Bar'を参照するのではなく、ネストされた型を指しています。ネストされた型は* member *であり、囲み型は完全でなければなりません。 –

1

あなたは私が提供したものよりもはるかに優れた回答を受け入れました。私は私のものを取り除いています。しかし、あなたの事例をさらに減らすことに興味があると思いました。私はあなたの元のコードに戻って関連付けるAとBとして2つの行をマークしました。あなたがそれらを反転すると、あなたの例のように、コンパイルは失敗します。

template<typename T> 
struct First 
{ 
     typedef typename T::type type; 
}; 

struct Second 
{ 
     typedef int type; // (A) 
     First<Second> order; // (B) 
}; 


int main(int argc, char** argv) 
{ 
     Second x; 
}; 
関連する問題