Last Update 2023/12/12
概要
各種テンプレート例(変数、関数、クラス)
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
GCC-12.2.0
GNU C Library 2.36
GNU Binutils 2.39
コード例・出力内容中の表記
・実行例中の太字表記部分は、コマンドなどの入力された文字列を示します。
・「︙」や「...」の着色省略表記は、 実際のソースコードや出力内容などを省略加工した部分を示します。
・「︙」や「...」の着色省略表記は、 実際のソースコードや出力内容などを省略加工した部分を示します。