テンプレートのインスタンス化には、次の2つの「段階」しかありません。
- デフォルト(即時)、
- 延期(怠惰)。
デフォルトは、次のメンバーに使用されます。
typedef
s、- メンバーフィールド。
延期:
- 機能(静的かどうか)、
- ネストされたタイプ。
これは正しいですか、それともテンプレートをインスタンス化するコンパイラの熱意に関する他のいくつかの落とし穴/ルールがありますか?
完全に間違っています。テンプレートのインスタンス化には1つの段階しかありません。いつ(そしてどこで、これは重要なポイントです)は、それがどのように使用されるかによって異なります。名前の検索とインスタンス化を混同していると思います。
名前の検索は2つのフェーズで行われます。(少なくともコンパイラーが適合している場合、VC ++はまだこれを間違っています。)最初は、コンパイラーがテンプレート定義を解析したときに発生します。テンプレートがインスタンス化されたときの2番目。シンボルが最初のフェーズで検索されるか、2番目のフェーズで検索されるかは、シンボルが依存しているかどうかによって異なります。これを決定するルールはかなり複雑ですが、一般的には次のようになります。
それが関数名であり、その引数の1つ以上がテンプレート引数に依存している場合、関数名は依存しており、インスタンス化の前ではなく、インスタンス化時に検索されます(および関数のオーバーロード解決)。
名前がテンプレート引数(たとえば
T::something
、T
はテンプレート引数)で修飾されている場合、それは依存しています。
クラステンプレートのメンバーで、依存する基本クラス(何らかの方法でテンプレート引数に依存する基本クラス)がある場合、の右側にあるものはすべてthis->
依存しています。
(実際のルールはかなり複雑ですが、上記は大まかな概算であり、ほとんどの用途にはおそらく十分です。)
違いのほんの一例:
class MyType {};
void func0( double d )
{
std::cout << "called func0( double )" << std::endl;
}
void func1( double, MyType const& )
{
std::cout << "called func1( double )" << std::endl;
}
template <typename T>
int funcT( T const& param )
{
func0( 42 ); // non-dependent
func1( 42, param ); // dependent
}
void func0( int d )
{
std::cout << "called func0( int )" << std::endl;
}
void func1( int, MyType const& )
{
std::cout << "called func1( int )" << std::endl;
}
int
main()
{
funcT( MyType() );
return 0;
}
出力は次のようになります
called func0( double )
called func1( int )
func0
inの呼び出しfuncT
は依存関係がないため、名前の検索はテンプレートが定義された時点でのみ発生し、
後でインスタンス化されるときは発生しません。その時点では、が1つしかないfunc0
ため、呼び出されます。
func1
inの呼び出しfuncT
は依存しているため(2番目の引数はテンプレート引数に依存しているため)、インスタンス化の時点(main
この場合は直後)で追加の名前ルックアップが行われます。この追加のルックアップはADNのみを使用しますが、引数の1つがグローバル名前空間で定義されているため、ADNはグローバル名前空間を検索し、の2番目の宣言も検索しfunc1
ます(より適切な一致であるため、オーバーロード解決によって選択されます) )。
現時点では、壊れているVC ++ 11にしかアクセスできないため、これを確認できないことに注意してください。そしてそれは微妙なので、私が何かを逃した可能性は十分にあります。一般的なルールは、そのようなあいまいさを避けることです。
func0
テンプレート定義の前に宣言されていない場合、コードはコンパイルされないことに注意してください。経験上、先行標準コンパイラ(またはVC ++)から移行する場合、これがエラーの最も一般的な原因です。
これが時々驚く別の一般的なケース:
template <typename Base>
class Derived : public Base
{
public:
Derived()
{
init(); // Non-dependent lookup, will NOT find any
// function init() in Base!!!
this->init() // Dependent lookup, WILL find
// Base::init, if it exists.
Base::init() // Also dependent.
}
};
通常、このパターンを使用していて、VC ++などの先行標準コンパイラで開発している場合、最新のコンパイラに移行すると、多くのコンパイラエラーが発生します。を追加することで簡単に修正できますthis->
。ただし、テンプレートの定義時に
グローバル関数も表示される場合は、基本クラスの関数ではなく、これを呼び出します。関数に意味のある名前を選択し、グローバル関数を名前空間に配置すると、このセマンティクスのサイレントな変更に頻繁に遭遇することはないでしょうが、注意が必要です。init