頭がどうにかなりそうなのでまとめる。
template < template < typename > class C >
struct test {};
このように、テンプレートパラメータの中にテンプレート型の情報を入れるとテンプレート型を受け取ることができる。
実際にこの test
にテンプレート型を渡してみる。
template < typename T >
struct A {};
...
test< A< int > >; //Error
このような渡しかたは間違い。以下のようなエラーが出る。
Template argument for template template parameter must be a class template or type alias template
これが正解
test< A >; //OK
なぜだろうか。
A< int >
を渡してしまった場合、それはすでにテンプレート型ではなく A< int >
型になっている。
テンプレートテンプレートパラメータは、テンプレート型を受け取るのであって、型を受け取るのではない。
テンプレート型を保ったまま渡すには A
を渡す必要がある。
受け取ったテンプレート型は、内部でテンプレート型から型に変換されて利用されるものであることを意識する。
template < template < typename T > class C >
test {
C<int>;
...
C<float>;
...
};
A< int >
などを渡したい場合はどうすればいいかA< int >
はすでに型である。テンプレート型ではない。ということに注目すればこうなることが予測できるはず。
template < typename T >
struct test {};
template < typename T >
struct A {};
...
test< A< int > >; // OK
普通の型として受け取ってしまえばよい。
テンプレート型を受け取る部分は、テンプレート型として特殊化しなければならない。すなわち
template < typename T, typename U >
struct A {};
template < typename T, typename U >
struct B {};
...
template < template < typename, typename > class Temp >
struct test{
static constexpr bool value = false;
};
//Aで特殊化
template <>
struct test < A >{
static constexpr bool value = true;
};
...
std :: cout << test < A > :: value << std :: endl; //true
std :: cout << test < B > :: value << std :: endl; //false
このような方法で特殊化することができる。
通常のテンプレートテンプレートパラメータとほとんど同じように利用できる。
template < int N, typename T >
struct A { };
template < template < int, typename > class Type >
struct test { };
...
test< A >; // OK
テンプレート型の第1パラメータが int
型に変化し、
テンプレートテンプレートパラメータも template < int, typename > class Type
と、第1パラメータに int
を受け取るように変化している。
template < int N, typename T >
struct A { };
template < template < typename, typename > class Type >
struct test2 { };
...
test2< A >; //Error
これはできない。以下のようなエラーが出る。
Template template argument has different template parameters than its corresponding template template parameter
上記の
template < template < int, typename > class Type >
struct test { };
に、以下のようなものを渡すことができるだろうか
//基底クラス
template < typename T, typename U >
struct A { };
//継承しつつ、第1パラメータを int 固定
template < typename T >
struct inherit_A : A< int, T > { };
//エイリアステンプレートで第1パラメータを int 固定
template < typename T >
using using_A = A< int, T >;
template < template < int, typename > class Type >
struct test { };
ぱっと見ではいけそうな気がするが、
test< inherit_A >; //Error
test< using_A >; //Error
両方とも不可能。
Template template argument has different template parameters than its corresponding template template parameter
パラメータを固定したとしても template < typename T, typename U >
な
テンプレート型であるとみなされていることになるのだろうか。
であるならば test
を元に戻したら問題なく動作するはずだが、
template < template < typename, typename > class Type >
struct test { };
...
test< inherit_A >; //Error
test< using_A >; //Error
これも動作しない。
Template template argument has different template parameters than its corresponding template template parameter
同様のエラーが出る。となると
template < typename Type >
struct test { };
....
test< inherit_A >; //Error
test< using_A >; //Error
これもエラー。だが、
Use of class template 'inherit_A' requires template arguments
Use of alias template 'using_A' requires template arguments
文章が変化した。テンプレートパラメータが足りない。
template < typename Type >
struct test { };
....
test< inherit_A <char> >; //OK
test< using_A <long> >; //OK
これは通る。テンプレートパラメータを1つでも確定させると、 もうテンプレート型ではなく型になってしまうようだ。 だが型としても不十分で、完全な型になるために テンプレートパラメータが足りていないとエラーが出ていたみたいだ。
どういうことかというと、
template < template < typename T , typename U > class C >
struct test {
T type; //Error
U type2; //Error
};
内側の山カッコ内の名前は利用できない。よくよく考えてみると
template < typename T >
struct A {
T type; //OK
};
T global_type; //NG
当たり前だが、クラスや関数内でテンプレートパラメータを利用できるが外側では利用できない。
今回も template < typename T , typename U > class C
の外側でテンプレートパラメータを利用しようとしているので、
それは無理だということらしい。
Temp<...>
型で特殊化するtemplate < typename T >
struct test { };
を、
template < typename T >
struct A { };
test < A < int > >;
test < A < char > >;
test < A < float > >;
と、 A<...>
といった形で特殊化したい場合、どうすればよいか。
//基底クラスA
template < typename T, typename U >
struct A {};
//第1パラメータの int 固定
template < typename T >
using int_A = A< int, T >;
//A <int, float> 型
using int_float_A = A< int, float >;
//別の基底クラスB
template < typename T, typename U >
struct B { };
//testの基底
template < typename T >
struct test {
static constexpr bool value = false;
};
//Temp<int, ...> で特殊化
template < template < typename, typename > class Temp, typename Type >
struct test < Temp< int, Type> > {
static constexpr int value = 10;
};
//Temp<int, float> で特殊化
template < template < typename, typename > class Temp >
struct test < Temp< int, float > > {
static constexpr int value = -1;
};
// B<...> で特殊化
template < typename T, typename U >
struct test < B< T, U > >{
static constexpr int value = 100;
};
int main(int argc, const char * argv[]) {
std :: cout << test < A< char, char > > :: value << std :: endl; //testの基底呼び出し
std :: cout << test < int_A< int > > :: value << std :: endl; //Temp<int, ...> 特殊化呼び出し
std :: cout << test < int_float_A > :: value << std :: endl; //Temp<int, float> 特殊化呼び出し
std :: cout << test < B< float, float > > :: value << std :: endl; //B<...> 特殊化呼び出し
std :: cout << test < B< char, char > > :: value << std :: endl; //B<...> 特殊化呼び出し
return 0;
}
テンプレート型がひな形になった型の特殊化、ということで、まずどのようなテンプレート型がひな形になっているか指定する。
template < template < typename, typename > class Temp, ... >
ここでいくつのテンプレートパラメータ、非型テンプレートパラメータを持つテンプレート型がひな形であるかが確認できる。
次にテンプレートパラメータのまま残しておくパラメータを、特殊化のテンプレートパラメータとして取得する。 これが必要なのは、前述のようにテンプレートテンプレートパラメータのパラメータ名は利用できないため。
template < template..., typename Type >
ここまでを指定して、どのようなテンプレート型 派生の型に対する特殊化であるかを明示する。
struct test < Temp< int, Type > > ...
struct test < Temp< int, float > > ...
そもそも、あるテンプレート型がひな形の場合、という特殊化はこうなる。
template < typename T, typename U >
struct test < B< T, U > >{
static constexpr int value = 100;
};
で、完全な型でなければ呼び出しが行なえないので、完全な型にして呼び出しを行う。
std :: cout << test < A< char, char > > :: value << std :: endl; //基底の呼び出し
std :: cout << test < int_A< int > > :: value << std :: endl; // Temp<int, ...> 型の特殊化
std :: cout << test < int_float_A > :: value << std :: endl; // Temp<int, float> 型の特殊化
std :: cout << test < B< float, float > > :: value << std :: endl; // B<...> 型の特殊化
std :: cout << test < B< char, char > > :: value << std :: endl;
テンプレート型を受け取ることはできないのでこういうのはエラーとなる。
std :: cout << test< A > :: value << std :: endl;
他の特殊化と区別をつけることができない場合、曖昧で解決できないというエラーが起こる。
std :: cout << test < B< int, char > > :: value << std :: endl;
この場合、Temp<int, ...>
の特殊化と B<...>
の特殊化、どちらを使えばいいか判断できなくてエラーが出る。
Ambiguous partial specializations of 'test<B<int, char> >'
0
10
-1
100
100