【tips】いまさらテンプレート(template)

# 備忘録です

```.cpp
//クラステンプレート
template<typename T = int> class A { };
//関数テンプレート
template<typename T = int> void f(T a) { }
//エイリアステンプレート(型の別名)
template<typename T = int> using B = A<T>;	//typedefキーワードではなくusingキーワードを使用する
//変数テンプレート
template<typename T = int> T x;		// C++14から(デフォルト引数はC++17から)
```

(どのサイトにも似たような説明が書かれているので、詳細は省きます)


# テンプレートの型推論

推論がない場合、A\<int>のように\<型>が必要になります。推論は引数や初期値から型を特定します。

```.cpp
template<typename T> class A {	//コンストラクタなし(C++17以前は推論なし,C++20からは初期値を使って推論可能)
public:
    T m1;
};
template<typename T> class B {	//コンストラクタ引数の型からT型を推論する
public:
    T m1;
    B(T a) { m1 = a; }
};
template<typename T> T f(T a) { return a; }	//関数の引数からT型を推論する

int main()
{
    A<int> a1;	//メンバー変数m1はint型(推論なし)
    A<char> a2;	//メンバー変数m1はchar型(推論なし)
    B b1(1);		//メンバー変数m1はint型(引数から型を推論する)
    B b2('1');		//メンバー変数m1はchar型(引数から型を推論する)
    B<double> b3(1);	//メンバー変数m1はdouble型(double型を指定(推論なし))

    f(1);			//引数はint型(引数から型を推論する)
    f<double>(1);	//引数はdouble型(推論なし)

    //初期値から型を推論する(C++17から) ※1
    B a3 { '1' };	//メンバー変数m1はchar型
    B a4 = { '1' };	//メンバー変数m1はchar型
    B a5 = { 2.0 };	//メンバー変数m1はdouble型
    //(C++20から) ※2
    A aa1 { 1 };
    A aa2 = { '1' };
    A aa3 = { 2.0 };
}
```

## 推論補助

```.cpp
template<typename T> class B {
public:
    typedef T u_type;
};

namespace CCC {
template<class T1> class A {
public:
    T1 m1;
    template<class T2> A(T2 x) {}
};

//推論補助
//・Aクラスと同じ名前空間(例ではCCC)に記述する必要がある
//・Aクラスの外に記述する必要がある
template<class T3> A(T3) -> A<typename T3::u_type>;
} // namespace CCC

int main()
{
    B<int> b;
    CCC::A a(b);	//1.コンストラクタ引数からテンプレートT2をB<int>型と推論する
                    //2.B<int>型を推論補助を使ってテンプレートT1はB<int>::u_type型(すなわちint型)と推論する
}
```

## 推論補助で何が嬉しいの？

コンテナの要素型を意識することなくテンプレートにできます(※1のURLに例があります)。


※1. [クラステンプレートのテンプレート引数推論](https://cpprefjp.github.io/lang/cpp17/type_deduction_for_class_templates.html)
※2. [集成体クラステンプレートのテンプレート引数推論](https://cpprefjp.github.io/lang/cpp20/class_template_argument_deduction_for_aggregates.html)


# テンプレートの特殊化

```.cpp
//【クラステンプレートの特殊化】
//A.ジェネリックなクラステンプレート
template<typename T1, typename T2> class A { public : A(T1 a, T2 b) {;} };
//B.完全特殊化(bool型とfloat型に特殊化したクラステンプレートの定義)
//  ジェネリックなクラステンプレートが無いとエラー
template<> class A<bool, float> { public :A(bool a, float b) {;}};
//C.部分特殊化(T1をbool型に特殊化したクラステンプレートの定義)
//  ジェネリックなクラステンプレートが無いとエラー
template<typename T> class A<bool, T> { public : A(bool a, T b) {;} };
//D.部分特殊化(T2をbool型に特殊化したクラステンプレートの定義)
//  ジェネリックなクラステンプレートが無いとエラー
template<typename T> class A<T, bool> { public : A(T a, bool b) {;} };

//【関数テンプレートの特殊化】
//A.ジェネリックな関数テンプレート
template<typename T1, typename T2> void f(T1 a, T2 b) { }
//B.完全特殊化(bool型とfloat型に特殊化した関数テンプレート)
//  ジェネリックな関数テンプレートが無いとエラー
template<> void f(bool a, float b) { }
//C.部分特殊化ではない(Tのみジェネリックな関数テンプレート)
//  単独でもエラーにならない
template<typename T> void f(bool a, T b) { }
//D.部分特殊化ではない(Tのみジェネリックな関数テンプレート)
//  単独でもエラーにならない
template<typename T> void f(T a, bool b) { }

int main()
{
    A<bool, double> a1(true, 2);    //C.が呼ばれた(想像ではA.が呼ばれると思ってた)
    //A<> a3(true, 2.0f);   //コンパイルエラー
    A a4(true, 2.0f);       //B.が呼ばれる
    A a2(true, 2.0);        //C.が呼ばれる
    A a5(true, 'a');        //C.が呼ばれる
    A a6('a', true);        //D.が呼ばれる

    f<bool, double>(true,2);    //A.が呼ばれる
    f<>(true, 2.0f); //C.が呼ばれた(想像ではB.が呼ばれると思ってた)
    f(true, 2.0f);   //C.が呼ばれた(想像ではB.が呼ばれると思ってた)
    f(true,2.0);     //C.が呼ばれる
    f(true, 'a');    //C.が呼ばれる
    f('a', true);    //D.が呼ばれる

    //感じとして、呼び方の規則性が曖昧です。テンプレートをコメントアウトすることで
    //自動で呼び先を変えてくれるのはいいのですが、意図しないものが呼ばれないように注意が必要です。
    //この結果はバージョンやコンパイラーによって変わるのかもしれません。
}
```

```.cpp
//【エイリアステンプレートの特殊化】
template<typename T1, typename T2> class A { public : A(T1 a, T2 b) {;} };
template<typename T1, typename T2> using B = A<T1, T2>;	//通常のエイリアステンプレート
//template<> using B = A<bool, float>;     		//完全特殊化はコンパイルエラー
//template<typename T> using B = A<bool, T>;    //部分特殊化はコンパイルエラー

//【変数テンプレートの特殊化】
template<typename T1, typename T2> T1 x;	//通常の変数テンプレート
template<> bool x<bool, float>;             //完全特殊化(※1)
template<typename T> T* x<T, float>;		//部分特殊化(C++23から？※1)

//【変数テンプレートの特殊化の実用的な使い方？】
template<typename T> T zero = 0;				//C++23から※1
template<typename T> T* zero<T*> = nullptr;		//C++23から※1

//エイリアステンプレートは特殊化できない
//変数テンプレートは特殊化できる(C++23から)

int main()
{
    int a = zero<int>;      //0
    int* b = zero<int*>;    //null
}
```

※1.実際にコンパイルしてみると g++ -std=c++17でコンパイルが通る
[変数テンプレートの部分特殊化を許可](https://cpprefjp.github.io/lang/cpp23/generalized_wording_for_partial_specializations.html)


# 可変引数テンプレート

```.cpp
template <typename... Args> class A { };
template <typename... Args> void f(Args... args);
```
詳細は、[可変引数テンプレート](https://cpprefjp.github.io/lang/cpp11/variadic_templates.html)参照のこと

# 非型テンプレートパラメータ

テンプレートパラメータには普通は型を指定します。条件次第で値をパラメータにすることもできます。

```.cpp
class Z {};
template<typename T, T V> class A { T m1 = V; };
template<typename T> class B { T m1; };
template<int V> class C { decltype(V) m1 = V; };
//auto宣言(C++17)
template<auto V> class D { decltype(V) m1 = V; };	//decltype(V)でV値の型を取得
//クラス型を許可する(C++20)
template<Z V> class E { decltype(V) m1 = V; };

int main()
{
    Z z;
    A<int, 3> a;	//型と値を指定できる
    //B<3> b1;      //コンパイルエラー
    B<int> b2;		//これだと値を指定できない
    C<3> c1;		//メンバー変数m1の型はint型(値は3)
    C<true> c2;		//メンバー変数m1の型はint型(値は1)
    C<'c'> c3;		//メンバー変数m1の型はint型(値は99)
    //C<3.0> c4;   	//コンパイルエラー(浮動小数点数は渡せない)
    D<3> d1;   		//C++17から<auto V>が使える
    D<true> d2;		//メンバー変数m1の型はbool型(値はtrue)
    D<'c'> d3;		//メンバー変数m1の型はchar型(値は'c')
    //D<3.0> d4;   	//コンパイルエラー(浮動小数点数は渡せない)
    E<z> e;         //C++20から
}
```

[非型テンプレートパラメータのauto宣言](https://cpprefjp.github.io/lang/cpp17/declaring_non-type_template_arguments_with_auto.html)
[非型テンプレートパラメータとしてクラス型を許可する](https://cpprefjp.github.io/lang/cpp20/class_types_in_non-type_template_parameters.html)


# typenameキーワード

```.cpp
//コンパイラはtypenameとclassを区別しない
template<typename T> void f() {}
template<class T>    void f() {}
```

昔はclassキーワードを使ってた。typenameキーワードは、C++98/03で規格化。
C++14以前のC++ではテンプレートテンプレートパラメータにtypenameキーワードを利用することができなかった。


# 長くなるので説明を割愛

・テンプレートテンプレートパラメータ
・[任意の式によるSFINAE](https://cpprefjp.github.io/lang/cpp11/sfinae_expressions.html)
・[畳み込み式](https://cpprefjp.github.io/lang/cpp17/folding_expressions.html)
・[using宣言のパック展開](https://cpprefjp.github.io/lang/cpp17/pack_expansions_in_using.html)
・[コンセプト](https://cpprefjp.github.io/lang/cpp20/concepts.html)
・[継承コンストラクタからのクラステンプレート引数の推論](https://cpprefjp.github.io/lang/cpp23/class_template_argument_deduction_from_inherited.html)

# さいごに
テンプレートは機能が豊富です。すべての機能を備忘録に書くには多すぎます。詳しく知りたい方は[cpprefjp - C++日本語リファレンス](https://cpprefjp.github.io)をおすすめします。(言語機能のページに、C++11,C++14,C++17,C++20,C++23,C++26とバージョン毎にまとめられ、わかりやすく解説されていますがC++11より前の説明は見当たりません)