C++のテンプレート関係は難しくて、もともとのC++との整合性もあって複雑になっている。std::decayは配列とポインタの関係を扱ってテンプレートをより汎用的にするためのもの。
前提知識、配列はコピーできない
1 2 3 4 |
int a[] = {1,2,3}; int b[3]; // compile error // b = a; |
前提知識、配列は配列で初期化できない
1 2 3 |
int a[] = {1,2,3}; // compile error // int a2[3](b); |
前提知識、ポインタならコピーできる
1 2 3 |
int a[] = {1,2,3}; int* c; c = a; |
コピーできると言ってもポインタにアドレスをコピーしているだけ。
前提知識:関数もコピーできない
配列と同様に関数もコピーできないけど、関数へのポインタならコピーできる。
1 2 3 4 5 6 7 8 9 10 |
void f1(){} void f2(){} ... { // compile error // f2 = f1; void (*f3)(); f3 = f1; } |
std::decay
こんな事があるので、テンプレートで型を汎用的に扱おうとすると一部の型でできなくなることがある。このできなくなる型の場合にできる型に変換してくれるのがstd::decay。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
template<class T> void tf(const T& t) { const T tmp(t); std::cout << tmp << std::endl; } template<class T> void tf2(const T& t) { typename std::decay<const T>::type tmp(t); std::cout << tmp << std::endl; } ... { tf(1); // compile error: // tf("aaa"); tf2(1); tf2("aaa"); } |
“aaa”というリテラル文字列は型としてchar[4]なのでそのままだと初期化できない。そこでdecayを使ってこれをポインタ型に変えるとコピーできる。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
void f1(){} void f2(){} template<class T> void tg(T& t) { T tmp(t); tmp(); } template<class T> void tg2(T& t) { typename std::decay<T>::type tmp(t); tmp(); } ... { // error // tg(f1); tg2(f1); } |
生関数のままの型だとコピーできないのでエラーになるけどdecayを使って関数へのポインタに変換すればコピーできる。