業務でつまづいたことメモ。
テンプレートパラメータの宣言でclassとtypenameの違いはないが、typenameにはもう一つの意味があって、これを使わなければならない場所がある。例えば、STLコンテナを引数に取る関数テンプレートを作る時、
1 2 3 4 5 |
template <typename T> void func(const T& container) { if(container.size() > 2) { T::const_iterator iter(container.begin()); // 型名とは解釈されないのでエラー ... |
C++には、テンプレート内にあるネストされた依存名は、特に指示されていなければ型名とは解釈されないというルールがある。T::const_iteratorを型名と解釈させるためにはコンパイラに指示を与える必要があるが、ここで使うのがtypename。
1 2 3 4 5 |
template <typename T> void func(const T& container) { if(container.size() > 2) { typename T::const_iterator iter(container.begin()); // 依存型名の頭にtypenameを付ける ... |
typenameはネストされた依存名でのみ使う。
1 2 3 |
template <typename C> // typenameを使う(classでもよい) void f(const C& container, // typenameは使えない typename C::iterator iter); // typenameが必要 |
ただし、派生クラスを定義するときの基底クラスの指定と、初期化子リスト内での基底クラスの指定ではtypenameは使わないので注意。
1 2 3 4 5 6 7 8 9 10 11 |
template <typename T> class Derived:public Base<T>::Nested { // 基底クラスの指定ではtypenameを使えない public: explicit Derived(int x) // 初期化子リスト内での基底クラスの指定ではtypenameを使えない :Base<T>::Nested(x) { typename Base<T>::Nested temp; // ネストされた依存名なのでここではtypenameが必要 ... } ... }; |
参考:Effective C++ 42項