0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

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

Last updated at Posted at 2024-04-12

備忘録です

.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. クラステンプレートのテンプレート引数推論
※2. 集成体クラステンプレートのテンプレート引数推論

テンプレートの特殊化

.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でコンパイルが通る
変数テンプレートの部分特殊化を許可

可変引数テンプレート

.cpp
template <typename... Args> class A { };
template <typename... Args> void f(Args... args);

詳細は、可変引数テンプレート参照のこと

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

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

.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宣言
非型テンプレートパラメータとしてクラス型を許可する

typenameキーワード

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

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

長くなるので説明を割愛

・テンプレートテンプレートパラメータ
任意の式によるSFINAE
畳み込み式
using宣言のパック展開
コンセプト
継承コンストラクタからのクラステンプレート引数の推論

さいごに

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

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?