Clangは、クラステンプレートのネストされたクラスが特殊化によってのみ定義されているコードを拒否することは正しいですか?


17

次のクラステンプレートがあるとします。

template<typename T>
struct Outer
{
    struct Inner;

    auto f(Inner) -> void;
};

Inner専門化ごとに個別に定義しますOuter

template<>
struct Outer<int>::Inner {};

template<>
struct Outer<double>::Inner {};

次にf、すべての特殊化についてメンバー関数を1回定義しますOuter

auto Outer<T>::f(Inner) -> void
{

}

しかしClang(9.0.0)は不平を言います:

error: variable has incomplete type 'Outer::Inner'

auto Outer<T>::f(Inner) -> void

                      ^

Inner他のすべての特殊化の定義も提供することにより、コンパイラエラーを回避できますOuter

template<typename T>
struct Outer<T>::Inner {};

または、f専門化ごとに個別に定義することにより:

template<>
auto Outer<int>::f(Inner) -> void
{

}

template<>
auto Outer<double>::f(Inner) -> void
{

}

GCCとMSVCはどちらも最初のコードを受け入れます。これはClangのバグですか、それとも3つのうちの唯一の適合実装ですか?

コンパイラエクスプローラを試す


Innerの特殊化は無関係であり、それらを削除してもコンパイル結果は変わりません。
n。「代名詞」m。

@n。 '代名詞' m。どういう意味かわかりません。どちらの定義追加するInner他のすべての専門分野のために定義しf、各専門分野のために個別に解決コンパイルエラー。
組み込み

もう一度読んでみましょう。それらを削除してもコンパイル結果は変わりません。追加せず、削除。gcc clang
n。「代名詞」m。

@n。 '代名詞' m。私はあなたが今何を意味するかを理解していますが、それはまだ奇妙なコメントです。私の質問の要点は、提供されているInnerそれぞれの専門分野の定義にもかかわらず、不完全な型として報告されているというOuterことでした。Innerその定義を削除すると、明らかに(正しく)不完全なタイプになります。
組み込み

「あなたはその定義(複数可)を削除すると明らかにインナーは(正確に)不完全型になります。」「まったくckearじゃないん何のことをAの専門は完全に別のテンプレートであり、それはすべての主要なテンプレートには影響しません。。
n。「代名詞」

回答:


4

Clangがコードを拒否するのは間違っていると思います。関数の宣言と定義は、

auto f(typename T::Inner) -> void;

// ...

template<typename T>
auto Outer<T>::f(typename T::Inner) -> void
{ }

この例でT::Innerは、明らかに依存型です。したがって、Clangはインスタンス化するまでそれが不完全であると想定しない場合があります。あなたの例でも同じことが言えますか?私はそう言うでしょう。標準でこれを持っているので:

[temp.dep.type]

5名前は、現在のインスタンス化のメンバーである場合、

  • ルックアップ時に、現在のインスタンス化であるクラスの少なくとも1つのメンバーまたはその非依存基本クラスを指す非修飾名。[注:これは、クラステンプレートの定義で囲まれたスコープで名前を検索する場合にのみ発生します。—エンドノート]
  • ...

名前が現在のインスタンス化のメンバーである場合名前は、現在のインスタンス化のメンバーであり、検索すると、現在のインスタンス化であるクラスの少なくとも1つのメンバーを参照します。

9タイプは、依存している場合

  • ...
  • 不明な専門分野のメンバー
  • 現在のインスタンス化の依存メンバーであるネストされたクラスまたは列挙、
  • ...

したがって、パラグラフ9の最初の箇条書きがケースをカバーしていtypename T::Innerます。それは依存型です。

その間、あなたの事件は2番目の弾丸によってカバーされます。Outer::Innerは、の現在のインスタンス化で見つかった名前でありOuter、さらにOuter、基本クラスではなく、それ自体の中にあります。これにより、現在のインスタンス化の依存メンバーになります。この名前は、ネストされたクラスを指します。つまり、2番目の箇条書きのすべての条件が適用されるため、Outer::Inner、依存型もれます。

どちらの場合にも依存型があるため、コンパイラーはそれらを依存型として同等に扱う必要があります。私の結論は、GCCとMSVCは正しいということです。


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