目次

江添本 23章 p.263~

形式

template<typename T>
/* 宣言 */

例)

template<typename T>
T twice(T n) {
  return n * 2;
}

int main() {
  twice(123); // Tはint
  twice(1.23); // Tはdouble
}
  • テンプレートを使うときに自動でテンプレート引数を推定してくれる
  • <T>を使うことで明示的にテンプレート引数をT型に指定することもできる
    • twice<int>(0): Tはint
    • twice<double>(0): Tはdouble int型0からdouble型0.0への変換が行われる

テンプレート引数は型ではなく整数型の値を渡すこともできる

ただし、テンプレート引数はコンパイル時にすべて決定されるため、
テンプレート引数に渡せる値はコンパイル時に決定できるものでなければならない
例)

template<int N>
void f() {
  std::cout << N;
}

int main() {
  f<0>(); // テンプレート引数Nに0が渡る
  f<123>(); // Nに123が渡る

  // コンパイル時に決定できるためOK
  f<1+1>();

  int x{};
  std::cin >> x;
  // コンパイル時に決定できないためNG(エラー)
  f<x>();
}

クラステンプレート

テンプレートはクラスにも使える

  • 関数テンプレートは関数の前にテンプレートを書く
template<typename T> // テンプレート
void f(); // 関数
template<typename T, T N>
T value() {
  return N;
}

int main() {
  value<int, 1>();
  value<short, 1>();
}
  • 同様にクラステンプレートはクラスの前にテンプレートを書く
template<typename T> // テンプレート
stuct S {}; // クラス
template<typename T, std::size_t N>
struct array {
  T storage[N];

  T& operator [](std::size_t i) {
    return storage[i];
  }
};

簡単なstd::arrayが完成

ネストされた型名

エイリアス宣言(using): 型名に別名をつける機能

int main() {
  using number = int; // numberをintのエイリアスとして宣言
  number x = 123;
}
struct S {
  using number = int;
  number data;
};

int main() {
  S s{123};
  S::number x = s.data;
}
  • クラスの中で宣言されたエイリアス宣言による型名をネストされた型名という
  • std::arrayではテンプレート引数を直接使う代わりにネストされた型名が使われている
template<typename T, std::size_t N>
struct array {
  using value_type = T;
  using reference = T&;
  using size_type = std::size_t;

  value_type storage[N];

  reference operator [](size_type i) {
    return storage[i];
  }
};

こうすることで、T&のようなわかりにくい型ではなくreferenceのようにわかりやすい名前を使える
さらに、クラス外部から使うこともできる

int main() {
  using array_type = std::array<int, 5>;
  array_type a = {1, 2, 3, 4, 5};
  array_type::value_type x = 0;
  array_type::reference ref = a[0];
}

もちろんautoで書くこともできる

int main() {
  using array_type = std::array<int, 5>;
  array_type a = {1, 2, 3, 4, 5};
  auto x = 0;
  auto ref = a[0];
}

std::arrayのメンバー関数size()を実装

template<typename T, std::size_t N>
sturct array {
  using value_type = T;
  using reference = T&;
  using size_type = std::size_t;

  value_type storage[N];

  reference operator [](size_type i) {
    return storage[i];
  }

  size_type size() {
    return N;
  }
};

メンバー関数のconst修飾

  • constを付けた変数は値を変更できなくなる
  • 変更する必要のない場面でうっかり変更することを防いでくれる便利な機能
  • arrayは大きいので関数の引数として渡すときにコピーするのは非効率
  • → コピーを防ぐためリファレンスで渡す

std::array<T, N>を受け取って要素をすべて出力する関数を定義

template<typename Container>
void print(const Container& c) {
  for (std::size_t i = 0; i < c.size(); ++i) {
    std::cout << c[i];
  }
}

int main() {
  std::array<int, 5> a = {1, 2, 3, 4, 5};
  print(a);
}