読者です 読者をやめる 読者になる 読者になる

テンプレートのメモ

c++

基本

型推論

  • 関数テンプレートの場合は型推論されるので,実体化の際に明示的に型を書かなくても良い.
  • クラステンプレートの場合には推論されないので注意.

特殊化

  • テンプレート関数,テンプレートクラスがある時に特定の型でだけ別の動きをしたい場合には,その型専用のテンプレートを作れば特殊化される.
  • あるテンプレートを具象化すること(普通にテンプレートの実態?を作ること)も特殊化と言う場合があって混乱するので注意.
#include <iostream>
#include <string>

using namespace std;

template <typename X>
X add(X a, X b){ return a+b;}

// 特殊化
template<>
double add(double a, double b) {return a+b;}

int main()
{
  cout << add<int>(1, 2) << "\n";
  cout << add(1, 2) << "\n";                // 型が推論される
  cout << add<float>(1.0, 2.0) << "\n";
  cout << add(1.0, 2.0) << "\n";            // 同様
  cout << add<string>("Oda", "Nobunaga") << "\n";
  //cout << add("Oda", "Nobunaga") << "\n"; // これはエラー.char*になって'+'が無いと怒られる.
  return 0;
}

値引数,値パラメタ

  • テンプレートの宣言,定義に型ではなくて値を書く例を見る.例えば下記.hoge1, hoge2はそれぞれNが1,2で実態が作られる.
  • 実体化するインスタンス毎に設定値を変えたい場合に使うのかな?とも思うけどテンプレートでやらなくても普通にコンストラクタでやれば?と思う.どういう時に使うんだろう?コンパイル時に設定値も分けて実態を作りたい時かな?
template<int N>
class Hoge {
private:
  int m_ = N;
public:
  int get_m() const {return m_;}  
};

int main()
{
  Hoge<1> hoge1;
  Hoge<2> hoge2;

  cout << hoge1.get_m() << "\n";
  cout << hoge2.get_m() << "\n";
  return 0;
}

引数を柔軟に取るためのテンプレート

  • 何という名前のテクニックなのか知らないけどたまに見る.
  • ある関数やメンバ関数に与える引数を,その引数が適切に定義されていれば型の正式な名前に依らずに受け取りたい時とかに使える.
  • 例えば,色々な人(Human)の定義が作り得るとして,それらの人全般を受け取るような関数を作る時.下記例は,Human型はsay_helloというメンバ関数さえあれば,どんな型のものであろうと渡すことが出来る.実際に,NobunagaもIeyasuもHumanとは関係ないけど,say_helloがあるのでHumanの満たすべき性質は満たせている.
template<typename Human>
void call_say_hello(Human human) {human.say_hello();}

class Nobunaga {
public:
  void say_hello() {cout << "I'm Nobunaga.\n";}
};

class Ieyasu {
public:
  void say_hello() {cout << "I'm Ieyasu.\n";}
};

int main()
{
  Nobunaga nobunaga;
  Ieyasu   ieyasu;
  call_say_hello(nobunaga);
  call_say_hello(ieyasu);
  return 0;
}