template引数にポインタ

More than 3 years have passed since last update.

全然知りませんでしたが、template引数に一部のポインタ(参照)が入れられるそうです。いまいち使い方は分かりません。


おさらい

C++のtemplateと言えば普通こんな感じで何らかの型(type)が入ります。

template<typename T> void hoge(T& t){ std::cout << t << std::endl; }

ちょっと詳しい人なら整数(integral or enumeration type)が入れられることも知っていると思います。階乗をコンパイル時に計算する例がいろんなところで紹介されています。

template<int N> int intTpl(){ return N; }

typedef enum{
FOO,
BAR,
} FooBar;
template<FooBar fb> bool enumTpl(){ return fb == FOO; }

ちなみに脱線ですが、階乗などの"計算"のためにtemplateを使うのは可読性の面でやり過ぎという意見が最近では出始めています。コンパイル時に計算を済ます方法としてはC++11以降constexprが用意されていて、そちらの方が素直に書けます。特にC++14ならほぼ普通の(=実行時の)書き方と同じでコンパイル時計算を記述できるようです。


ポインタをtemplate引数に

さて本題です。以下のように一部のポインタ(参照)をtemplate引数に与えることができます。"一部の"という条件は、簡単に言えば「コンパイル時にコードが生成できるもの=コンパイル時にアドレスが静的に決まるもの」となるようです。

template<double* X> void dptr(){ std::cout << "value: " << *X << std::endl; }

double global_x = 0.3;

void test(){
double local_x = 0.3;

dptr<&global_x>();
// dptr<&local_x>(); // error
}

ブロック内のポインタはコンパイル時点ではアドレスが決まらないのでtemplate関数の展開時にコード生成ができず、コンパイルエラーです。一方大域に出している変数はアドレスが決まるのでOKです。

ではこんなもの何に使うかというと、どうやら関数のセット。

typedef void (*FuncA)(int i, int j);

template<FuncA F>
struct FuncTpl{
static void call(int i, int j) { (*F)(i, j); }
};

void funcA(int i, int j){ std::cout << "funcA" << std::endl; }
void funcB(int i) { std::cout << "funcB" << std::endl; }

void test(){
FuncTpl<funcA>::call(2, 3);
// FuncTpl<funcB>::call(2, 3); // error
}

あまりいい例が思いつかなかったので無意味な例ですが、おそらくCRTPやType Erasureと似た効果を発揮するような気がします。…と思ったけどアドレスが引けるかどうかを吟味するってことはinline展開は起きないんでしょうかね。この書き方が「一番書きやすい」か「一番早い」という例が見つかれば良いのですが。

おまけでconst char*に関してはどうなるのかを試しましたが、あんまり期待通りにはなりませんでした。

template<const char* Str> void print(){

std::cout << Str << std::endl;
}

const char hw_obj[] = "Hello World";
const char* hw_ptr = "Hello World";

void test(){
// print<"const char">(); // error
print<hw_obj>(); // needs C++11
// print<hw_ptr>(); // error
}