typename というキーワードには、2通りの用法がある。
"static" もそうだが、同一のキーワードに複数の意味を持たせるのは、
C++ の悪い癖だな。なんとかしてほしいところ。
で、typename の一つの意味は、テンプレート引数として
「型」を取ることを宣言する場合。
これは、"class" キーワードで置換できる。
たとえば、次の (A) と (B) は同値である。
template<typename T> class Foo; // (A) template<class T> class Foo; // (B)
まあ、これは誰でも知っていると思う。
typename には、もう一つ、後続する識別子が
「型名」であることを明示するという重要な働きがある。たとえば、
template<class T> class Foo {
public:
T::value_type foo() const;
};
のようなテンプレートの定義があったとして、コンパイラはこれを解析できるだろうか?
T に何か具体的なクラス名が与えられていれば解析可能だろうが、
いきなりこのテンプレートの定義を見せられても、T が何者であるか不明な時点では、
T::value_type も果たして何かの型であるかどうかは分からない、と言うべきであろう。
あるいは次の例。
template<class T> class Foo {
public:
void foo() {
T::value_type *p;
}
};
ここで太字にした文は、ポインタ p の宣言ではなくて、
T::value_type と p の乗算とみなされてしまう。
このような時には、プログラマがコンパイラに対して
「T::value_type は何かの型を表しているぞよ」
ということを教えてやる必要がある。そのためのキーワードが
typename なのである。
template<class T> class Foo {
public:
typename T::value_type foo() const;
};
template<class T> class Foo {
public:
void foo() {
typename T::value_type *p;
}
};
(2002-07-04 注記) しかしながら、Microsoft の VC++ だと、typename を付けなくても正しくコンパイルできてしまう。 VC++ って、妙なところで賢いなあ。逆に、おもに VC++ を対象にソースを書いている人は、 意識して typename を付けるようにしましょう。
この typename の例は、STL の iterator_traits あたりを見ると、
山ほど出てくる。たとえば、次の通り。
template<class Iterator> struct iterator_traits {
typedef typename Iterator::value_type value_type;
};
STL のソースを読むときなど、ちょっと注意してみるとよいだろう。
初出: 2002年5月14日