右上➚

プログラミングに関するメモをのこしていきます

C++ テンプレートの種類と構文

前回テンプレートがなぜ必要なのかについて簡単にまとめたので、今回はその構文や種類についてまとめたいと思います。

agtn.hatenablog.com

アウトライン

  • テンプレートの種類と構文
  • クラステンプレート
  • 関数テンプレート
  • メンバテンプレート
  • エイリアステンプレート(C++11〜)
  • 変数テンプレート(C++14〜)
  • まとめと今後

テンプレートの種類と構文

C++ のテンプレートは,大きく 5 種類に分類することが出来ます。

  1. クラステンプレート
  2. 関数テンプレート
  3. メンバ関数テンプレート
  4. エイリアステンプレート
  5. 変数テンプレート

これらについて、以降で詳しくまとめていきます。

まずざっくり共通するシンタックスを示しておきます。

定義する

何かのテンプレートを定義したい場合は、通常の定義の前に template 宣言を記述します。

template <typename T>
class stack {
  ...
};

複数のテンプレート引数を取りたい場合や、型以外のテンプレート引数を取りたい場合には、以下のようにします。

template <typename T, int N>
class my_array {
  ...
};

上の例はクラステンプレートでしたが、関数でもエイリアスでも、template を宣言する部分は共通です。

使用する(インスタンス化)

次にテンプレートを使用する場合です。
テンプレートはあくまでテンプレートなので、使用する際には、具体的なテンプレート引数を与えて実体化する必要があります。これを インスタンス といいます。

インスタンス化の構文も、テンプレートの種類によらず基本的には共通しています。

stack<int> int_stack;
my_array<std::string, 5> five_strings;

テンプレート名 < 引数 > という感じです。
一応注意書きをしておきますと、stack<stack<int>>シンタックスエラーになる場合があります。 意味としては stack<int> のスタックです。
stack<stack<int>>コンパイルエラーになる場合は、使っているコンパイラが古いかもしれません。 C++03 では、>> の部分がシフト演算子として解釈されてしまうためです。
g++ -std=c++11 とか clang++ -std=c++11 とか g++ -std=c++14 とか clang++ -std=c++14 とか、コンパイラが新しい規格を参照するようにオプションを渡してあげれば動きます。
(もし動かない場合はコンパイラが古すぎます。もう 2016 年ですから、最低でも C++11 以降を使いましょう。別物です。)

では、以降、それぞれのテンプレートについて詳しく見ていきます。

クラステンプレート

クラステンプレートは、クラスのテンプレートです。前回の stack がこれにあたります。
一番わかり易いユースケースは、コンテナ型の定義でしょう。
スタックや単方向リスト、ハッシュマップなど、内部に保持する型に依存しないデータ構造を定義するために使えます。

前回の stack を再掲しておきます。

template <typename T>
class stack {
public:
  stack() : data(), n() {}

  void push(T x) {
    if (n >= MAX_ELEM) {
      throw "stack is full!!";
    }
    data[n++] = x;
  }

  T pop() {
    if (n < 0) {
      throw "stack is empty!!";
    }
    return data[--n];
  }
private:
  T data[MAX_ELEM];
  int n;
};

関数テンプレート

関数テンプレートは以下の様なものです。

template <typename T>
T max(T left, T right) {
  if (left > right) {
    return left;
  } else {
    return right;
  }
}

int x = max<int>(1, 0); // => x == 1
double y = max<double>(0.5, 100.0); // => y == 100.0

max 関数は、「ある型 T について、2つの引数のうち、大きい方を返す」関数です。
明示的に max<int>max<double> とすることで、intdouble についての「大きい方を返す」関数を得ています。

テンプレート引数の推論

実は、関数テンプレートの場合、明示的にテンプレートを引数を渡す必要がない場合があります。
今回の max 関数はまさにそのケースです。

int x = max(1, 0);

これは、テンプレート引数の型推論の結果です。
max 関数の第一引数、第二引数はそれぞれ T です。そして、実際に渡されている 10int 型です。
これらの情報から、コンパイラT == int であることを推論します。
したがって暗黙に max<int> と指定されることになります。
この推論は色々複雑だったりしますが、はじめは引数から単純に推論できれば推論されると思っておけば良いんじゃないかなと思います。

一方、クラステンプレートなど、関数テンプレート(とメンバ関数テンプレート)以外のテンプレートの場合には、この推論は行われません。
勘違いしやすいので気をつけましょう。特にクラステンプレートは間違いやすいです。

メンバ関数テンプレート

class printer {
public:
  explicit printer(std::ostream& os) : os(os) {}

  template <typename T>
  void print(T const& v) {
    os << v;
  }
private:
  std::ostream& os;
};

メンバ関数テンプレートは、関数テンプレートとほとんど同じです。違いは、メンバ関数として定義されていることだけです。

printer p(std::cout);

ここまではテンプレートでもなんでもないただのクラスです。

p.print(0);
p.print("abc");
p.print<double>(0.1);

こんな感じで使います。関数テンプレートとほぼ同じですね。

エイリアステンプレート

エイリアステンプレートは、C++11 から使用できるテンプレートです。

template <typename T, typename U>
struct pair;

template <typename T>
using with_int_t = pair<T, int>;

using で型名のエイリアスを作ることが出来ますが、それをテンプレートにすることが出来ます。

with_int_t<bool> p(true, 0);  // pair<bool, int> p(true, 0);
with_int_t<std::string> s("abc", 100);  // pair<std::string, int> s("abc", 100);

ちなみに C++11 より前は、エイリアステンプレートの代替として、

template <typename T>
struct with_int {
  typedef pair<T, int> type;
}

with_int<bool>::type p(true, 0); // pair<bool, int> p(true, 0);

という記述をしていました。(今でもライブラリなどで現役の表現ですので覚えておきましょう)

変数テンプレート

最後の変数テンプレートは、C++14 から使用できるテンプレートです。

template <typename T>
constexpr T pi = static_cast<T>(3.1415926);

int x = pi<int>;
double y = pi<double>;

constexpr は定数式というやつです。

ちなみに C++14 より前は、代替として

template <typename T>
struct pi {
  static const T value = static_cast<T>(3.1415926);
};

int x = pi<int>::value;
double y = pi<double>::value;

という記述をしていました。(こちらも現役の表現です)

まとめと今後

というわけで今回はテンプレートの種類とそれぞれのシンタックスについてまとめてみました。
今後、特殊化や部分特殊化などのお話をするときに種類によって微妙に違いがあったりするので、しっかり区別しておいたほうが良さそうです。