これらのキーワードは次のトークンがタイプなのかテンプレートなのかを指定する。テンプレート関数内でテンプレート引数Tに依存するトークンが出てきたとき、コンパイラはそのトークンがタイプなのかテンプレートなのか関数なのか変数なのかわからない。
1 |
T::A<0>(x); |
Aが変数なら、例えば0に置き換えてみると以下のようになる。
1 |
0<0>(x); |
<や>は小なりと大なりと解釈され、有効な式になる。
Aが関数の場合は<0>はテンプレート引数となりxを引数とする関数コールになる。
Aがテンプレートクラスの場合はそのクラスインスタンスxの定義になる。
テンプレートが実体化されたときにわかりそうだが、意図した動作と違う時にエラーの発見が難しくもなりそうなので、Tに依存するトークンでそれがあいまいな場合(どんな時あいまいなのかはよくわからない)はtypenameやtemplateといったキーワードをつける約束になっているようだ。この例の場合はAはintなどの基本タイプにはなれない。
このような用途で使うのがtypenameとtemplate。
typename
T::Aがタイプの場合に指定する。これはよく使う。意味不明なエラーが出た場合につけると直ることが多い。
template
カギカッコなしのtemplateは他の使い方もあるが、ここではTに依存する(Tの中にある)テンプレートクラスやテンプレート関数を指定するときにつけるようだ。テンプレートクラスの場合はタイプでもあるので、両方つけることもある。
1 |
typename T::template A<0>(x); |
こういうものをつけなければならないときはusingを使って別名にしておいた方がいいだろう。
実験したまとめ
Aが変数の場合
typenanmeもtemplateもつけてはいけない(つけるとエラー)
Aが関数テンプレートの場合
typenameとtemplateと両方つけるか、何もつけない(typenameだけだとエラー)
Aがクラステンプレートの場合
両方つけないとエラー
ソース
https://github.com/ambiesoft/blogprogs/tree/master/6151/templatetypename
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 |
#include <iostream> struct S1 { static constexpr int A = 0; }; struct S2 { template<int N> static void A(int) {} }; struct S3 { template<int N> struct A {}; }; // Uncomment one of three // #define SISS1 #define SISS2 // #define SISS3 #ifdef SISS1 template <class T> void foo() { //// error C2903: 'A': symbol is neither a class template nor a function template //typename T::template A<0>(x); //// error C7511: 'A': 'typename' keyword must be followed by a qualified name //typename A<0>(x); int x = 0; T::A<0>(x); } int main() { foo<S1>(); } #endif #ifdef SISS2 template <class T> void foo() { int x = 0; typename T::template A<0>(x); T::A<0>(x); //// error C7511: 'A': 'typename' keyword must be followed by a qualified name //typename A<0>(x); } int main() { foo<S2>(); } #endif #ifdef SISS3 template <class T> void foo() { typename T::template A<0>(x); //// error C7511: 'A': 'typename' keyword must be followed by a qualified name //typename A<0>(x); //int x = 0; //// error C2371: 'x': redefinition; different basic types //T::A<0>(x); //// error C3861: 'x': identifier not found //T::A<0>(x); } int main() { foo<S3>(); } #endif |