12

単純な CRTP パターンが標準で有効かどうかを理解しようとしています。

以下のコードはコンパイルされ、(clang で) 期待どおりに動作します。

しかし、関連する標準の章/段落についての私の理解では、仮想関数 CRTP< Derived, Base >::DoSomething() のインスタンス化のポイントは、コードのポイント (B) にある必要があります。ここでは、Derived の完全な宣言はありません。利用可能。したがって、内側の typedef Type も使用できないはずです。

このコードを検証する関連する標準の章を誰か親切に指摘できますか?

つまり、この場合、仮想関数がインスタンス化された ATFER ポイント C であるということですか? 洞察をお寄せいただきありがとうございます。

フランチェスコ

//-------------------------
// START CODE

#include <iostream>

struct Type1 {};
struct Type2 {};

struct Base
{
  virtual ~Base() {}
  virtual void DoSomething() = 0;
};

template< typename T, typename U >
struct CRTP : U
{
  virtual void DoSomething() { DoSomething( typename T::Type() ); }

 void DoSomething( Type1 ) { std::cout << "1\n"; }
 void DoSomething( Type2 ) { std::cout << "2\n"; }
};

// (A) point of inst. of CRTP< Derived, Base > ( 14.7.1.4 ) ??
// (B) point of inst. of CRTP< Derived, Base >::DoSomething() (14.6.4.1.4 ) ??

struct Derived : CRTP< Derived, Base >
{
  typedef Type2 Type;
};

// (C)

int main()
{
  Base *  ptr = new Derived;
  ptr->DoSomething();
  delete ptr;
}

// END CODE
//-------------------------

関連する (?) 標準段落:

14.6.4.1 4 仮想関数が暗黙的にインスタンス化される場合、そのインスタンス化のポイントは、それを囲むクラス テンプレートの特殊化のインスタンス化のポイントの直後です

14.7.1 4 クラス型が完全に定義されたオブジェクト型を必要とするコンテキストで使用される場合、またはクラス型の完全性がプログラムのセマンティクスに影響を与える可能性がある場合、クラス テンプレートの特殊化は暗黙的にインスタンス化されます。

14.7.1 9 実装は、関数テンプレート、メンバー テンプレート、非仮想メンバー関数、メンバー クラス、またはインスタンス化を必要としないクラス テンプレートの静的データ メンバーを暗黙的にインスタンス化してはなりません。仮想メンバー関数がインスタンス化されない場合、実装がクラス テンプレートの仮想メンバー関数を暗黙的にインスタンス化するかどうかは指定されていません。

4

2 に答える 2

5

CRTP<Derived, Base>::DoSomething()これは、許可されているように、コンパイラが のインスタンス化を翻訳単位の終わりまで遅らせた結果のようです ( CWG issue 993を参照)。

CRTP<Derived, Base>Derived(§14.6.4.1 [temp.point]/p4、すべての引用符は N3936 に対するものです)の定義の直前に確実にインスタンス化されます。

クラス テンプレートの特殊化、クラス メンバー テンプレートの特殊化、またはクラス テンプレートのクラス メンバーの特殊化の場合、特殊化が別のテンプレートの特殊化内から参照されるために暗黙的にインスタンス化される場合、特殊化が参照されるコンテキストが依存する場合テンプレート パラメーターで、特殊化が外側のテンプレートのインスタンス化の前にインスタンス化されていない場合、インスタンス化のポイントは外側のテンプレートのインスタンス化のポイントの直前になります。それ以外の場合、そのような特殊化のインスタンス化のポイントは、特殊化を参照する名前空間スコープの宣言または定義の直前にあります。

インスタンス化する必要があるかどうかは、メンバー定義が存在する必要があるコンテキストで参照されるCRTP<Derived, Base>::DoSomething()句の意味に依存します(§14.7.1 [temp.inst]/p2)。すべての非純粋仮想関数は ODR で使用され (§3.2 [basic.def.odr]/p2)、「すべてのプログラムには、そのプログラムで ODR で使用されるすべての非インライン関数または変数の定義が 1 つだけ含まれる必要があります」 (§3.2 [basic.def.odr]/p4); それが「メンバー定義の存在を必要とするコンテキストで参照されている」と見なされるかどうかは不明です。

(ただし、インスタンス化する必要がない場合でも、コンパイラは§14.7.1 [temp.inst]/p11に従って自由にインスタンス化できます-「実装が暗黙的に仮想メンバー関数をインスタンス化するかどうかは指定されていません。仮想メンバー関数がインスタンス化されない場合は、クラス テンプレート。".)

実際にインスタンス化されている場合CRTP<Derived, Base>::DoSomething()、状況は §14.6.4.1 [temp.point]/p5 および p8 (強調鉱山) でカバーされます。

5 仮想関数が暗黙的にインスタンス化される場合、そのインスタンス化のポイントは、それを囲むクラス テンプレートの特殊化のインスタンス化のポイントの直後です。

8関数テンプレート、メンバー関数テンプレート、またはクラステンプレートのメンバー関数または静的データメンバーの特殊化は、翻訳単位内に複数のインスタンス化ポイントを持つことができ、上記のインスタンス化ポイントに加えて、任意の翻訳単位内にインスタンス化のポイントを持つ特殊化の場合、翻訳単位の最後もインスタンス化のポイントと見なされます。クラス テンプレートの特殊化は、翻訳単位内に最大 1 つのインスタンス化ポイントを持ちます。テンプレートの特殊化には、複数の翻訳単位でインスタンス化のポイントがある場合があります。インスタンス化の 2 つの異なるポイントが、1 つの定義規則 (3.2) に従ってテンプレートの特殊化に異なる意味を与える場合、プログラムは形式が正しくなく、診断は必要ありません。

つまり、インスタンス化の 2 つのポイントがあり、1 つは のインスタンス化のポイントの直後、もう 1CRTP< Derived, Base >つは翻訳単位の最後にあります。この場合、インスタンス化の 2 つのポイントで名前を検索typename T::Typeすると異なる結果が生成されるため、プログラムの形式が正しくなく、診断は必要ありません。

于 2014-09-09T04:13:39.417 に答える