Class Templateでの型推論

function templateの型推論

function templateでは関数呼び出し時の引数によって型が推論されます

template<typename T>
T pow(T a,T b)
   return (b!=1) ? a : pow(a*a,b-1);
}

main(){
   int a=3,b=3
   pow(a,b);  //pow<int>と推論される
}

class templateの型推論できない?

一方でclass templateは推論することができません
というか,引数とかないので推論するための要素をクラス外から与えられないです

しかしながら、ある特定の条件下では最小のtemplate引数で複数の型を推論させることができます
それがtemplate引数にclass templateを投げた場合です
これによって、ある程度class templateを利用するときの冗長性を排除する事ができます

class templateの冗長性

例えばContainerクラス(containerはvectorなどのSTL)を引数に取り
その先頭の要素(T型)の値を返すメンバ関数を持っている場合を考えます

以下のように書くとしましょう

template<typename Container,typename T> 
class N
{
  Container cont_;
  
  public:
  T get(){return cont_.front(); } 

};

int main()
{
   N<vector<int>,int> n;
}

まず、Containerがfront()を持っているのか?と言うのはコンパイル時に検出できるので無視します
問題はN<vector<int>,int>のようにintを2回書かなければならないのが、非常に冗長という事です
これを解決する手法としてテンプレートテンプレートパラメータにする手法が挙げられます

template< template<typename...> class Container,typename T>
class N
{
   Container<T> cont_;

  public:
  T get(){return cont_.front(); } 
}

int main()
{
   N<vector,int> n;
}

しかしこれだと元のclass templateや型引数が分からないtypedefされたコンテナを渡せなくなります
理想としては、vector<T>を渡しただけで、Tを推論してほしいところです。

class templateの部分的な型推論

class templateは与えられた型が何かのclass templateだった場合に
そのtemplateの型引数は推論することができます
(ちなみにこれを型推論と言ってよいのか、実はよくわかっておりません)

以下のように書くと勝手にvector<int>intTに(こちらが指定することなく推論して)割り当てます

template<typename Container>
class N
{
   N();
}

// いわゆるclass templateの部分特殊化
template<template<typename...> class Container,typename T, typename... Args> 
class N< Container<T,Args> >
{
  Container<T> cont_;
  
  public:
  T get(){ return cont_.front(); } 

};



int main()
{
   typedef vector<int> vec_int;
   N<vec_int> n;
}

上記のようにclass templateの部分特殊化を利用しています

はじめにclass templateだろうがただのclassだろうが
なんでも受取れるようにprimary class template
(特殊化ではないもともとのclass template)を定義します

そして部分特殊化版を書きます
今回書いているのはvectorとかlistとかに適合するやつです
こうするとvector<T>list<T>で部分特殊化されるようになります
さらにこの部分特殊化ではvector<int>intは型引数Tに自動的に割り当てられます

これによってSTLコンテナの型引数を自動判別させて、冗長なコードを少なくすることができます

ちなみに、誤って普通のクラスを型引数に代入し、
primary class templateとなってしまった場合を抑制するために コンストラクタに細工をするのを忘れないようにするとよいです
(上記はコンストラクタが生成できないようにしています)