テンプレートと名前検索を理解しようとする


9

次のコードスニペットを理解しようとしています

スニペット#1

template <typename T>
struct A
{
    static constexpr int VB = T::VD;
};

struct B : A<B>
{
};

ここでは、gcc9もclang9もエラーをスローしません。

Q.このコードがコンパイルされるのはなぜですか?A<B>Bから継承するときにインスタンス化していませんか?BにはVDがないので、コンパイラーはここでエラーをスローすべきではありませんか?

スニペット#2

template <typename T>
struct A
{
    static constexpr auto AB = T::AD; // <- No member named AD in B
};

struct B : A<B>
{
    static constexpr auto AD = 0xD;
};

この場合、gcc9は正常にコンパイルされますが、clang9は「BにADという名前のメンバーはありません」というエラーをスローします。

Q.なぜgcc9でコンパイルするのですか/なぜclang9でコンパイルしないのですか?

スニペット#3

template <typename T>
struct A
{
    using TB = typename T::TD;
};

struct B : A<B>
{
    using TD = int;
};

ここでは、clang9とgcc9の両方がエラーをスローします。gcc9は、「不完全な型 'struct B'の無効な使用」を示しています。

Q.構造Bがここで不完全な場合、スニペット#2で不完全でないのはなぜですか?

使用されるコンパイラフラグ:-std=c++17 -O3 -Wall -Werror。前もって感謝します!!!


@xceptionでstruct Bインスタンス化AしていませんBか?
可変の副作用

clang9は「BにADという名前のメンバーはありません」というエラーをスローしますB..不完全である...しかし、わからないメンバーがインスタンス化されなければならないとき
Jarod42

@MutableSideEffectああ、そうです、悪いことです。テンプレートとしても読んでください:(
xception

@ Jarod42では、なぜgccは正常にコンパイルされますか?
ミュータブルな副作用

1
私はこの質問に「もっと集中する必要がある」というフラグを立てましたが、質問には実際には複数の質問が含まれているため(私の結論です)、なぜ私のフラグが間違っているのですか?
ドミニク

回答:


4

これらは本質的に煮詰められると思います [temp.inst] / 2(強調は私のもの)にます

クラステンプレートの特殊化の暗黙的なインスタンス化は、宣言の暗黙的なインスタンス化を引き起こします が、クラスメンバー関数、メンバークラス、スコープメンバー列挙体、静的データメンバー、メンバーテンプレート、および定義のデフォルト引数、またはnoexcept-specifiers友達; […]

および[temp.inst] / 9

実装は、クラステンプレートの静的データメンバーを暗黙的にインスタンス化してはなりません[…]。

暗黙的なテンプレートのインスタンス化に関する標準の表現では、多くの詳細が解釈の余地があります。一般的に、テンプレートの一部に依存することはできないように思えますない仕様が明示的に述べていない限り、インスタンス化されています。したがって:

スニペット#1

Q.このコードがコンパイルされるのはなぜですか?Bから継承するときに、Aをインスタンス化しませんか?BにはVDがないので、コンパイラーはここでエラーをスローすべきではありませんか?

インスタンス化していますA<B>。ただし、インスタンス化A<B>は、静的データメンバーの定義ではなく、宣言のみをインスタンス化します。VB定義の存在を必要とするような方法で使用されることはありません。コンパイラはこのコードを受け入れる必要があります。

スニペット#2

Q.なぜgcc9でコンパイルするのですか/なぜclang9でコンパイルしないのですか?

Jarod42が指摘したように、の宣言にABはプレースホルダータイプが含まれます。ここで何が起こるかについて、標準の文言が本当に明確ではないように思えます。プレースホルダータイプを含む静的データメンバーの宣言のインスタンス化は、プレースホルダータイプの控除をトリガーし、静的データメンバーの定義を必要とする使用を構成しますか?私はそれに対してイエスかノーのどちらかをはっきりと言うような文言を標準で見つけることができません。したがって、私はどちらの解釈もここでは等しく有効であり、したがって、GCCとclangはどちらも正しいと思います…

スニペット#3

Q.構造Bがここで不完全な場合、スニペット#2で不完全でないのはなぜですか?

クラス型は}クラス指定子 [class.mem] / 6の終わりに達した時点でのみ完成します。したがって、すべてのスニペットののB暗黙的なインスタンス化中には不完全A<B>です。これがSnippet#1には無関係であったというだけのことです。スニペット#2ではNo member named AD in B、結果としてclangでエラーが発生しました。スニペット#2の場合と同様に、メンバーエイリアス宣言が正確にインスタンス化されるタイミングについての文言が見つかりません。ただし、静的データメンバーの定義とは異なり、クラステンプレートの暗黙的なインスタンス化中にメンバーエイリアス宣言のインスタンス化を明示的に防ぐための適切な表現はありません。したがって、GCCとclangの両方の動作は、この場合の標準の有効な解釈であると言えます…


ありがとう。この場合、本体内の静的データメンバーを初期化します。初期化は宣言の一部ですか、それとも静的データメンバーの定義の一部ですか?初期化が本体内にある場合、それは静的データメンバーの宣言の一部であるという印象を受けました。それが宣言の一部である場合、最初の引用では、クラステンプレートの周囲の暗黙的なインスタンス化の一部として、即時のインスタンス化が必要です。
Johannes Schaub-litb

仕様を調べたところ、C ++ 14とC ++ 17には違いがあるようです。C ++ 14では、constexpr静的データメンバーは単なる宣言でした。C ++ 17はinline変数を取得し、をconstexpr暗黙指定inlineします。これにより、本体の静的データメンバー宣言が定義になります。
Johannes Schaub-litb

eel.is/c++draft/dcl.spec.auto#4.sentence-2は、「プレースホルダータイプを使用して宣言された変数のタイプは、その初期化子から推定されます。この使用は、初期化宣言([dcl。 init])変数の。」したがって、静的データメンバーには初期化子が含まれているため、静的データメンバーの定義が必要であると私は主張します。
Johannes Schaub-litb

@ JohannesSchaub-litbこれを調べてくれてありがとう!私もこれらの質問について疑問に思っていましたが、決定的な言葉遣いを見つけることができませんでした。初期化と定義について、クラステンプレートの定義内のメンバー関数の定義を検討してください。このような定義も宣言であり、他に宣言はありません。ただし、クラステンプレートの暗黙的なインスタンス化は、宣言をインスタンス化するだけで、メンバー関数の定義はインスタンス化しません。静的データメンバーに同じことが当てはまらないのはなぜですか?
マイケルケンゼル

定義が必要なものがなければ、定義はインスタンス化されません。しかしこのauto場合、ルールは宣言が初期化宣言でなければならないことを示しています。これは、宣言が定義であることがわかっている場合にのみ当てはまります(私の知る限り..私はしばらくの間、弁護士の国を離れていました)。過去にも同様のケースがあり、eel.is / c ++ draft / temp.inst#2.sentence-3が追加されました。ここで、定義である宣言は、実際には「定義であることがわかっている」としてインスタンス化されます。定義をインスタンス化します。
Johannes Schaub-litb
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.