Loose-Info.com
Last Update 2023/12/12
TOP - 各種テスト - C++ - テンプレート

概要

各種テンプレート例(変数、関数、クラス)
型パラメータキーワード class・typename
複数の型パラメータを伴うテンプレート
非型パラメータ
デフォルトのテンプレートパラメータ
部分特殊化されたクラステンプレート
テンプレートパラメータとしてテンプレートを指定
クラステンプレート定義外でのメンバ関数定義
クラステンプレート内の静的データメンバ
クラス内の静的データメンバテンプレート
可変引数テンプレート
テンプレートのフレンド宣言
名前解決に関してのキーワード typename の使用


各種テンプレート例(変数、関数、クラス)


sample.cpp
#include <iostream> #include <iomanip> // 変数テンプレート template<class T> constexpr T x = T(1.23456789012345678L); // 関数テンプレート template<class T> T smpl(const T& x) { return -x; } // クラステンプレート template<class T> struct Smpl_t { T x; Smpl_t(T x0 = 0) : x(x0) {} void view() { std::cout << std::scientific << std::setprecision(17) << T(x) << std::endl;} }; int main() { std::cout << std::scientific << std::setprecision(17); std::cout << "変数テンプレート" << std::endl; std::cout << x<unsigned short> << std::endl; std::cout << x<float> << std::endl; std::cout << x<long double> << std::endl << std::endl; std::cout << "関数テンプレート" << std::endl; std::cout << smpl<int>(1.2345) << std::endl; std::cout << smpl<long double>(1.2345) << std::endl << std::endl; std::cout << "クラステンプレート" << std::endl; Smpl_t<int> s1(1.2345); Smpl_t<long double> s2(1.2345); s1.view(); s2.view(); }

実行結果
$ gcc -Wall sample.cpp -lstdc++ $ ./a.out 変数テンプレート 1 <--- 型引数としてunsigned shortを指定 1.23456788063049316e+00 <--- 型引数としてfloatを指定 1.23456789012345678e+00 <--- 型引数としてlong doubleを指定 関数テンプレート -1 <--- 型引数としてintを指定 -1.23449999999999993e+00 <--- 型引数としてlong doubleを指定 クラステンプレート 1 <--- 型引数としてintを指定 1.23449999999999993e+00 <--- 型引数としてlong doubleを指定

型パラメータキーワード
class
typename


sample.cpp
#include <iostream> #include <iomanip> // 型パラメータキーワード class template<class T> void func1() { T x = T(1.23456789012345678L); std::cout << "class T --- x = " << x << std::endl; } // 型パラメータキーワード typename template<typename T> void func2() { T x = T(1.23456789012345678L); std::cout << "typename T --- x = " << x << std::endl; } int main() { std::cout << std::scientific << std::setprecision(17); func1<int>(); func1<long double>(); func2<int>(); func2<long double>(); }

実行結果
$ gcc -Wall sample.cpp -lstdc++ $ ./a.out class T --- x = 1 class T --- x = 1.23456789012345678e+00 typename T --- x = 1 typename T --- x = 1.23456789012345678e+00

複数の型パラメータを伴うテンプレート


sample.cpp
#include <iostream> #include <iomanip> // 3つの型パラメータ template<class T, class U, class V> void func(T x, U y, V z) { T a = T(x); U b = U(y); V c = V(z); std::cout << "a = " << a << std::endl; std::cout << "b = " << b << std::endl; std::cout << "c = " << c << std::endl; } int main() { std::cout << std::scientific << std::setprecision(17); func<int, float, long double>(1.23456789012345678L, 1.23456789012345678L, 1.23456789012345678L); }

実行結果
$ gcc -Wall sample.cpp -lstdc++ $ ./a.out a = 1 b = 1.23456788063049316e+00 c = 1.23456789012345678e+00

非型パラメータ


sample.cpp
#include <iostream> // 非型パラメータ --- 整数値 template<class T, int X> void func1(T a) { T b = a * X; std::cout << "b = " << b << std::endl; } // 非型パラメータ --- 列挙型 enum en { ABC = 5, DEF, GHI }; template<class T, en X> void func2(T a) { T b = a * X; std::cout << "b = " << b << std::endl; } int main() { func1<long double, 5>(1.2345); func2<long double, DEF>(1.2345); }

実行結果
$ gcc -Wall sample.cpp -lstdc++ $ ./a.out b = 6.1725 b = 7.407

デフォルトのテンプレートパラメータ


sample.cpp
#include <iostream> #include <iomanip> // デフォルトのテンプレート引数を指定 template<class T = int, class U = double, class V = long double> void func() { T a = 1.23456789012345678L; U b = 1.23456789012345678L; V c = 1.23456789012345678L; std::cout << std::scientific << std::setprecision(17); std::cout << "a = " << a << std::endl; std::cout << "b = " << b << std::endl; std::cout << "c = " << c << std::endl; std::cout << std::endl; } int main() { std::cout << "型パラメータを指定して関数呼び出し" << std::endl; func<long double, int, double>(); std::cout << "型パラメータを指定無しで関数呼び出し" << std::endl; func(); }

実行結果
$ gcc -Wall sample.cpp -lstdc++ $ ./a.out 型パラメータを指定して関数呼び出し a = 1.23456789012345678e+00 b = 1 c = 1.23456789012345669e+00 型パラメータを指定無しで関数呼び出し a = 1 <--- デフォルトのパラメータを使用 b = 1.23456789012345669e+00 <--- 〃 c = 1.23456789012345678e+00 <--- 〃

部分特殊化されたクラステンプレート


sample.cpp
#include <iostream> #include <iomanip> // 特殊化されていないクラステンプレート template<class T> class Smpl { T *x; public: Smpl() { x = new T; } ~Smpl() { std::cout << x << " --- Destructor" << std::endl; delete [] x; } void f() { std::cout << *x << std::endl; } void fput(T y) { *x = y; } }; // ポインタ用に部分特殊化されたクラステンプレート template<class T> class Smpl<T*> { T *x; public: Smpl() : x(nullptr) {} ~Smpl() { std::cout << &x << " --- Destructor" << std::endl; } void f() { std::cout << *x << std::endl; } void fput(T *y) { x = y; } }; int main() { std::cout << std::scientific << std::setprecision(17); int xi = 1; double xd = 2.2; long double xl = 3.3; // 特殊化されていないクラステンプレートのインスタンス Smpl<int> si; Smpl<double> sd; Smpl<long double> sl; // 部分特殊化されたクラステンプレートのインスタンス Smpl<int*> spi; Smpl<double*> spd; Smpl<long double*> spl; // インスタンスに値を設定 si.fput(1.23456789012345678L); sd.fput(1.23456789012345678L); sl.fput(1.23456789012345678L); // インスタンスに変数へのポインタを設定 spi.fput(&xi); spd.fput(&xd); spl.fput(&xl); // 出力関数の呼び出し si.f(); sd.f(); sl.f(); spi.f(); spd.f(); spl.f(); std::cout << std::endl; // ポインタの指す変数の格納値を変更 xi = 4; xd = 5.5; xl = 6.6; // ポインタ用に特殊化されたインスタンスの出力関数の呼び出し spi.f(); spd.f(); spl.f(); }

実行結果
$ gcc -Wall sample.cpp -lstdc++ $ ./a.out 1 1.23456789012345669e+00 1.23456789012345678e+00 1 2.20000000000000018e+00 3.29999999999999982e+00 4 5.50000000000000000e+00 6.59999999999999964e+00 0x7ffd508665d0 --- Destructor 0x7ffd508665d8 --- Destructor 0x7ffd508665e0 --- Destructor 0x21e2ef0 --- Destructor 0x21e2ed0 --- Destructor 0x21e2eb0 --- Destructor

テンプレートパラメータとしてテンプレートを指定


sample.cpp
#include <iostream> template<class T> class A { T x; public: A() : x(T(10)) {} void f() { std::cout << x << std::endl; } }; // テンプレートパラメータとしてテンプレートを指定 template<template<class T> class U> class Smpl { U<int> x; public: void f() { x.f(); } }; int main() { Smpl<A> s; s.f(); }

実行結果
$ gcc -Wall sample.cpp -lstdc++ $ ./a.out 10

クラステンプレート定義外でのメンバ関数定義


sample.cpp
#include <iostream> #include <iomanip> // プライマリテンプレート template<class T> class Smpl { T *x; public: Smpl() { x = new T; } ~Smpl() { std::cout << x << " --- Destructor" << std::endl; delete [] x; } void f(); void fput(T y) { *x = y; } }; // テンプレート定義の外部で定義されたメンバ関数 template<class T> void Smpl<T>::f() { std::cout << *x << std::endl; } // 部分特殊化されたクラステンプレート template<class T> class Smpl<T*> { T *x; public: Smpl() : x(nullptr) {} ~Smpl() { std::cout << &x << " --- Destructor" << std::endl; } void f() { std::cout << *x << std::endl; } void fput(T *y); }; // テンプレート定義の外部で定義されたメンバ関数 template<class T> void Smpl<T*>::fput(T *y) { x = y; } int main() { std::cout << std::scientific << std::setprecision(17); int xi = 1; long double xl = 2.2; Smpl<int> si; Smpl<long double> sl; Smpl<int*> spi; Smpl<long double*> spl; si.fput(1.23456789012345678L); sl.fput(1.23456789012345678L); spi.fput(&xi); spl.fput(&xl); si.f(); sl.f(); spi.f(); spl.f(); }

実行結果
$ gcc -Wall sample.cpp -lstdc++ $ ./a.out 1 1.23456789012345678e+00 1 2.20000000000000018e+00 0x7ffef87c4f70 --- Destructor 0x7ffef87c4f78 --- Destructor 0x21c7ed0 --- Destructor 0x21c7eb0 --- Destructor

クラステンプレート内の静的データメンバ


sample.cpp
#include <iostream> #include <iomanip> // 静的データメンバを含むクラステンプレート template<class T> struct Smpl { static T x; static T f() { ++x; return x; } }; // 静的データメンバの定義 template<class T> T Smpl<T>::x = 10; int main() { std::cout << std::scientific << std::setprecision(17); std::cout << "静的データメンバ初期値" << std::endl; std::cout << "Smpl<int>::x = " << Smpl<int>::x << std::endl; std::cout << "Smpl<double>::x = " << Smpl<double>::x << std::endl << std::endl; std::cout << "関数呼び出し(1回目)" << std::endl; std::cout << "Smpl<int>::f() = " << Smpl<int>::f() << std::endl; std::cout << "Smpl<double>::f() = " << Smpl<double>::f() << std::endl << std::endl; std::cout << "関数呼び出し(2回目)" << std::endl; std::cout << "Smpl<int>::f() = " << Smpl<int>::f() << std::endl; std::cout << "Smpl<double>::f() = " << Smpl<double>::f() << std::endl << std::endl; }

実行結果
$ gcc -Wall sample.cpp -lstdc++ $ ./a.out 静的データメンバ初期値 Smpl<int>::x = 10 Smpl<double>::x = 1.00000000000000000e+01 関数呼び出し(1回目) Smpl<int>::f() = 11 Smpl<double>::f() = 1.10000000000000000e+01 関数呼び出し(2回目) Smpl<int>::f() = 12 Smpl<double>::f() = 1.20000000000000000e+01

クラス内の静的データメンバテンプレート


sample.cpp
#include <iostream> #include <iomanip> struct Smpl { // 静的データメンバテンプレート template<class T> static T x; }; // 静的データメンバテンプレートの定義 template<class T> T Smpl::x = { 1, 2 }; int main() { std::cout << std::scientific << std::setprecision(17); std::cout << "静的データメンバテンプレート初期値" << std::endl; std::cout << "Smpl::x<int[2]>[0] = " << Smpl::x<int[2]>[0] << std::endl; std::cout << "Smpl::x<int[2]>[1] = " << Smpl::x<int[2]>[1] << std::endl; std::cout << "Smpl::x<int[3]>[1] = " << Smpl::x<int[3]>[1] << std::endl; std::cout << "Smpl::x<int[3]>[2] = " << Smpl::x<int[3]>[2] << std::endl << std::endl; std::cout << "静的データメンバテンプレートの操作" << std::endl; std::cout << "++Smpl::x<int[2]>[0] = " << ++Smpl::x<int[2]>[0] << std::endl; std::cout << "++Smpl::x<int[3]>[0] = " << ++Smpl::x<int[3]>[0] << std::endl << std::endl; std::cout << "++Smpl::x<int[2]>[0] = " << ++Smpl::x<int[2]>[0] << std::endl; std::cout << "++Smpl::x<int[3]>[0] = " << ++Smpl::x<int[3]>[0] << std::endl << std::endl; }

実行結果
$ gcc -Wall sample.cpp -lstdc++ $ ./a.out 静的データメンバテンプレート初期値 Smpl::x<int[2]>[0] = 1 Smpl::x<int[2]>[1] = 2 Smpl::x<int[3]>[1] = 2 Smpl::x<int[3]>[2] = 0 静的データメンバテンプレートの操作 ++Smpl::x<int[2]>[0] = 2 ++Smpl::x<int[3]>[0] = 2 ++Smpl::x<int[2]>[0] = 3 ++Smpl::x<int[3]>[0] = 3

可変引数テンプレート


sample.cpp
#include <iostream> #include <iomanip> static int n = 0; // カウント用 // 1個のパラメータの出力のみを処理する関数テンプレート template<class T> void v(T x) { std::cout << "Type = " << typeid(T).name() << " --- args[" << n << "] = " << x << std::endl; n++; } // 2個以上のパラメータを処理する可変引数テンプレート template<class T, class... Args> void v(T x, Args... args) { // 最初のパラメータを処理する関数の呼び出し v<T>(x); // 2個目以降のパラメータパックを展開してこの関数テンプレートを再帰的に呼び出し v<Args...>(args...); } int main() { std::cout << std::scientific << std::setprecision(17); // 5個の関数引数で関数テンプレートを呼び出す v(1, 2, 1.23, 'a', 1.23456789012345678L); std::cout << std::endl; n = 0; // テンプレート引数を全てint型指定して関数テンプレートを呼び出す v<int, int, int, int, int>(1, 2, 1.23, 'a', 1.23456789012345678L); }

実行結果
$ gcc -Wall sample.cpp -lstdc++ $ ./a.out Type = i --- args[0] = 1 Type = i --- args[1] = 2 Type = d --- args[2] = 1.22999999999999998e+00 Type = c --- args[3] = a Type = e --- args[4] = 1.23456789012345678e+00 Type = i --- args[0] = 1 Type = i --- args[1] = 2 Type = i --- args[2] = 1 Type = i --- args[3] = 97 Type = i --- args[4] = 1 (注) typeid(T).name()で出力される型を示す文字は実装依存

テンプレートのフレンド宣言


sample.cpp
#include <iostream> #include <iomanip> class Smpl1 { int n; // 関数テンプレートをフレンドとして宣言 template<class T> friend void getn(Smpl1 *ps); // クラステンプレートをフレンドとして宣言 template<class T> friend class Smpl2; public: Smpl1() : n(1) {} }; // フレンド宣言されたクラステンプレート template<class T> struct Smpl2 { int n2; Smpl2() : n2(2) {} // Smpl1内のprivate指定されたメンバを使用 void getn2(Smpl1& rs) { std::cout << "Smpl2 : " << T(rs.n + n2) << std::endl; } }; // フレンド宣言された関数テンプレート // Smpl1内のprivate指定されたメンバを使用 template<class T> void getn(Smpl1 *ps) { std::cout << "getn : " << T(ps->n) << std::endl; } int main() { std::cout << std::scientific << std::setprecision(17); Smpl1 s1; Smpl2<int> s2; // テンプレート引数にint型を指定してインスタンスを生成 Smpl2<double> s3; // 〃 double型 〃 // フレンド宣言された関数テンプレートの呼び出し getn<int>(&s1); // テンプレート引数にint型を指定して関数を呼び出す getn<double>(&s1); // 〃 double型 〃 // フレンド宣言されたクラステンプレートのメンバ関数の呼び出し s2.getn2(s1); s3.getn2(s1); }

実行結果
$ gcc -Wall sample.cpp -lstdc++ $ ./a.out getn : 1 getn : 1.00000000000000000e+00 Smpl2 : 3 Smpl2 : 3.00000000000000000e+00

名前解決に関してのキーワード typename の使用


sample.cpp
#include <iostream> template<class T> struct Smpl { // テンプレートパラメータに依存する名前を型名として指定 typename T::type_char c{'A'}; }; struct A { // メンバとして型名type_charをchar型として定義 typedef char type_char; }; int main() { Smpl<A> s; std::cout << s.c << std::endl; }

実行結果
$ gcc -Wall sample.cpp -lstdc++ $ ./a.out A

実行環境

GNU bash, version 5.1.16
GCC-12.2.0
GNU C Library 2.36
GNU Binutils 2.39


コード例・出力内容中の表記

・実行例中の太字表記部分は、コマンドなどの入力された文字列を示します。
・「」や「...」の着色省略表記は、 実際のソースコードや出力内容などを省略加工した部分を示します。