C++ テンプレート | 2024.08.05 |
関数テンプレート |
mul()はテンプレートで指定する型の乗算を行う。テンプレート宣言のtypenameには任意の型を指定できるが、この例の場合ならば、その型で乗算ができなければエラーになる。
#include <iostream>
using namespace std;
template <typename T>
T mul(T a, T b)
{
T x = a * b;
return x;
}
int main()
{
cout << mul<double>(1.5, 0.7) << endl; // 1.05
cout << mul<int>(5, 8) << endl; // 40
return 0;
}
using namespace std;
template <typename T>
T mul(T a, T b)
{
T x = a * b;
return x;
}
int main()
{
cout << mul<double>(1.5, 0.7) << endl; // 1.05
cout << mul<int>(5, 8) << endl; // 40
return 0;
}
C++ テンプレート | 2024.08.05 |
クラスメンバ関数個別のテンプレート |
クラスのメンバ関数をテンプレートで定義する。
この例のmainで、<long>の部分を<short>などにしてもArrのset()とget()がそれに合わせて定義される。
この例のmainで、<long>の部分を<short>などにしてもArrのset()とget()がそれに合わせて定義される。
コンストラクタもテンプレートで、
template <typename T> Arr::Arr(int n)
のように定義して、
Arr arr<long>(5);
のように呼び出したいところだが、テンプレートでコンストラクタを呼び出すことは不可である。そのような場合ならば、クラスそのものをテンプレートで定義する「クラステンプレート」にした方がよいだろう。
template <typename T> Arr::Arr(int n)
のように定義して、
Arr arr<long>(5);
のように呼び出したいところだが、テンプレートでコンストラクタを呼び出すことは不可である。そのような場合ならば、クラスそのものをテンプレートで定義する「クラステンプレート」にした方がよいだろう。
#include <iostream>
#include <cstdlib>
using namespace std;
class Arr {
public:
Arr(size_t size, int n);
~Arr() { free(arrp);};
template <typename T> void set(T e, int i);
template <typename T> T get(int i);
private:
void* arrp;
};
Arr::Arr(size_t size, int n)
{
arrp = malloc(n * size);
}
template <typename T> void Arr::set(T e, int i)
{
*(T*)((T*)arrp + i) = e;
}
template <typename T> T Arr::get(int i)
{
return *(T*)((T*)arrp + i);
}
int main()
{
Arr arr(sizeof(long), 5);
arr.set<long>(10000L, 0);
arr.set<long>(20000L, 1);
cout << arr.get<long>(0) << endl;
cout << arr.get<long>(1) << endl;
}
#include <cstdlib>
using namespace std;
class Arr {
public:
Arr(size_t size, int n);
~Arr() { free(arrp);};
template <typename T> void set(T e, int i);
template <typename T> T get(int i);
private:
void* arrp;
};
Arr::Arr(size_t size, int n)
{
arrp = malloc(n * size);
}
template <typename T> void Arr::set(T e, int i)
{
*(T*)((T*)arrp + i) = e;
}
template <typename T> T Arr::get(int i)
{
return *(T*)((T*)arrp + i);
}
int main()
{
Arr arr(sizeof(long), 5);
arr.set<long>(10000L, 0);
arr.set<long>(20000L, 1);
cout << arr.get<long>(0) << endl;
cout << arr.get<long>(1) << endl;
}
C++ テンプレート | 2024.08.05 |
クラステンプレート |
クラスをテンプレートで定義する「クラステンプレート」。
メンバ関数の実装の関数宣言部分は、
template <typename T> T Arr<T>::get(int i)
のようにする。
クラスのスコープの部分が、Arr<T>::のようになることがメンバ関数テンプレートとは異なる。
メンバ関数の実装の関数宣言部分は、
template <typename T> T Arr<T>::get(int i)
のようにする。
クラスのスコープの部分が、Arr<T>::のようになることがメンバ関数テンプレートとは異なる。
クラステンプレートは、
クラス名<テンプレートパラメータ>
のようにテンプレートパラメータと共に指定した時点でクラスの実装が確定する。
#include <iostream>
#include <cstdlib>
using namespace std;
template <typename T>
class Arr {
public:
Arr(int n);
~Arr() { delete arrp;}
void set(T e, int i);
T get(int i);
private:
T* arrp;
};
template <typename T> Arr<T>::Arr(int n)
{
arrp = new T[n];
}
template <typename T> void Arr<T>::set(T e, int i)
{
arrp[i] = e;
}
template <typename T> T Arr<T>::get(int i)
{
return arrp[i];
}
int main()
{
Arr<long> arr(5);
arr.set(10000L, 0);
arr.set(20000L, 1);
cout << arr.get(0) << endl;
cout << arr.get(1) << endl;
}
#include <cstdlib>
using namespace std;
template <typename T>
class Arr {
public:
Arr(int n);
~Arr() { delete arrp;}
void set(T e, int i);
T get(int i);
private:
T* arrp;
};
template <typename T> Arr<T>::Arr(int n)
{
arrp = new T[n];
}
template <typename T> void Arr<T>::set(T e, int i)
{
arrp[i] = e;
}
template <typename T> T Arr<T>::get(int i)
{
return arrp[i];
}
int main()
{
Arr<long> arr(5);
arr.set(10000L, 0);
arr.set(20000L, 1);
cout << arr.get(0) << endl;
cout << arr.get(1) << endl;
}
C++ テンプレート | 2024.08.05 |
定義と実装を分けないクラステンプレート |
実装をクラス定義の中に組み込む場合のクラステンプレート。
プロトタイプと実装を分ける場合のように、関数定義ごとに改めてテンプレートを宣言しなおす必要がない。
プロトタイプと実装を分ける場合のように、関数定義ごとに改めてテンプレートを宣言しなおす必要がない。
クラステンプレートの定義だけをヘッダファイルに分離した場合、typenameで決定する実装部分が見つからないことが起こるので、定義と実装をまとめた方が扱いやすい。
#include <iostream>
using namespace std;
template <typename T>
class Arr {
public:
Arr(int n) {
arrp = new T[n];
}
~Arr() { delete arrp;};
void set(T e, int i) {
arrp[i] = e;
}
T get(int i) {
return arrp[i];
}
private:
T* arrp;
};
int main()
{
Arr<long> arr(5);
arr.set(10000L, 0);
arr.set(20000L, 1);
cout << arr.get(0) << endl;
cout << arr.get(1) << endl;
}
using namespace std;
template <typename T>
class Arr {
public:
Arr(int n) {
arrp = new T[n];
}
~Arr() { delete arrp;};
void set(T e, int i) {
arrp[i] = e;
}
T get(int i) {
return arrp[i];
}
private:
T* arrp;
};
int main()
{
Arr<long> arr(5);
arr.set(10000L, 0);
arr.set(20000L, 1);
cout << arr.get(0) << endl;
cout << arr.get(1) << endl;
}
C++ テンプレート | 2024.08.05 |
クラステンプレートの継承 |
クラステンプレートをスーパークラスにした継承。
クラステンプレートのArrを継承するStackクラスで、Arrのメンバ関数get()やset()を呼び出すときに、Arr<T>::のようにスコープ解決するのは、継承元クラスがテンプレートパラメータのTによって確定するからである。逆にサブクラス側のメンバ関数を呼び出すとき、場合によりthis->get()のようにしなければならないことがある。
クラステンプレートのArrを継承するStackクラスで、Arrのメンバ関数get()やset()を呼び出すときに、Arr<T>::のようにスコープ解決するのは、継承元クラスがテンプレートパラメータのTによって確定するからである。逆にサブクラス側のメンバ関数を呼び出すとき、場合によりthis->get()のようにしなければならないことがある。
#include <iostream>
using namespace std;
template <typename T>
class Arr {
public:
Arr(int n) {
arrp = new T[n];
}
virtual ~Arr() { delete arrp;}
void set(T e, int i) {
arrp[i] = e;
}
T get(int i) {
return arrp[i];
}
virtual void clear() = 0;
private:
T* arrp;
};
template <typename T>
class Stack : public Arr<T> {
public:
Stack(int n) : Arr<T>(n) { stacki = 0; }
void push(T e) {
Arr<T>::set(e, stacki++);
}
T pop() {
if (stacki == 0)
throw exception();
return Arr<T>::get(--stacki);
}
void clear() {
stacki = 0;
}
private:
int stacki;
};
int main()
{
Stack<long> st(5);
st.push(10000L);
st.push(20000L);
cout << st.pop() << endl;
cout << st.pop() << endl;
st.push(10000L);
try {
st.clear();
cout << st.pop() << endl;
} catch (exception e){
cout << "stack error\n";
}
}
using namespace std;
template <typename T>
class Arr {
public:
Arr(int n) {
arrp = new T[n];
}
virtual ~Arr() { delete arrp;}
void set(T e, int i) {
arrp[i] = e;
}
T get(int i) {
return arrp[i];
}
virtual void clear() = 0;
private:
T* arrp;
};
template <typename T>
class Stack : public Arr<T> {
public:
Stack(int n) : Arr<T>(n) { stacki = 0; }
void push(T e) {
Arr<T>::set(e, stacki++);
}
T pop() {
if (stacki == 0)
throw exception();
return Arr<T>::get(--stacki);
}
void clear() {
stacki = 0;
}
private:
int stacki;
};
int main()
{
Stack<long> st(5);
st.push(10000L);
st.push(20000L);
cout << st.pop() << endl;
cout << st.pop() << endl;
st.push(10000L);
try {
st.clear();
cout << st.pop() << endl;
} catch (exception e){
cout << "stack error\n";
}
}
C++ テンプレート | 2024.08.05 |
クラステンプレートの静的メンバ |
静的メンバを、グローバルスコープで実体を定義することはテンプレートの場合も同様である。テンプレートパラメータを与えることで実体となる静的メンバが各々定義される。
#include <iostream>
using namespace std;
template <typename T>
class Sumx {
public:
void set(T a) {
x = a;
sum += a;
}
void show() {
cout << "x=" << x << " sum=" << sum << endl;
}
private:
static T sum;
T x;
};
template <typename T>
T Sumx<T>::sum = 0;
int main()
{
Sumx<long> lsum;
lsum.set(10000L);
lsum.show(); // x=10000 sum=10000
lsum.set(500L);
lsum.show(); // x=500 sum=10500
Sumx<char> csum;
csum.set('1');
csum.set('0');
csum.show(); // x=500 sum=10500
return 0;
}
using namespace std;
template <typename T>
class Sumx {
public:
void set(T a) {
x = a;
sum += a;
}
void show() {
cout << "x=" << x << " sum=" << sum << endl;
}
private:
static T sum;
T x;
};
template <typename T>
T Sumx<T>::sum = 0;
int main()
{
Sumx<long> lsum;
lsum.set(10000L);
lsum.show(); // x=10000 sum=10000
lsum.set(500L);
lsum.show(); // x=500 sum=10500
Sumx<char> csum;
csum.set('1');
csum.set('0');
csum.show(); // x=500 sum=10500
return 0;
}
C++ テンプレート | 2024.08.05 |
複数のテンプレートパラメータ |
テンプレートの宣言では、複数のテンプレートパラメータを設置できる。
#include <iostream>
using namespace std;
template <typename T, typename U>
class Time {
public:
Time(T h, T m, T s) {
sec = h * 3600 + m * 60 + s;
}
U getsec() {
return sec;
}
private:
U sec;
};
int main()
{
Time<int, long> t(1, 35, 12);
cout << t.getsec() << endl; // 5712
}
using namespace std;
template <typename T, typename U>
class Time {
public:
Time(T h, T m, T s) {
sec = h * 3600 + m * 60 + s;
}
U getsec() {
return sec;
}
private:
U sec;
};
int main()
{
Time<int, long> t(1, 35, 12);
cout << t.getsec() << endl; // 5712
}
C++ テンプレート | 2024.08.05 |
デフォルトのテンプレートパラメータ |
テンプレートパラメータには、末尾から省略時のデフォルトを指定できる。テンプレートパラメータを省略してもデフォルトで定義が決定する。
#include <iostream>
using namespace std;
template <typename T = int, typename U = T>
class Time {
public:
Time(T h, T m, T s) {
sec = h * 3600 + m * 60 + s;
}
U getsec() {
return sec;
}
private:
U sec;
};
int main()
{
Time<int, long> t1(1, 35, 12);
Time<int> t2(2, 41, 25);
Time<> t3(2, 41, 25);
cout << t1.getsec() << endl; // 5712
cout << t2.getsec() << endl; // 9685
cout << t3.getsec() << endl; // 9685
}
using namespace std;
template <typename T = int, typename U = T>
class Time {
public:
Time(T h, T m, T s) {
sec = h * 3600 + m * 60 + s;
}
U getsec() {
return sec;
}
private:
U sec;
};
int main()
{
Time<int, long> t1(1, 35, 12);
Time<int> t2(2, 41, 25);
Time<> t3(2, 41, 25);
cout << t1.getsec() << endl; // 5712
cout << t2.getsec() << endl; // 9685
cout << t3.getsec() << endl; // 9685
}
C++ テンプレート | 2024.08.05 |
非型テンプレート |
テンプレートパラメータは、typenameによる型名(クラス名)以外に値そのものを指定できる。
非型のテンプレートに指定された値は定数として置き換えられる。インスタンス生成時の非型テンプレートパラメータに指定する値は、非型テンプレートパラメータの宣言の型でなければならない。テンプレートの実引数に指定する非型テンプレートパラメータの値が異なれば、異なる個別のクラス定義になる。
非型のテンプレートに指定された値は定数として置き換えられる。インスタンス生成時の非型テンプレートパラメータに指定する値は、非型テンプレートパラメータの宣言の型でなければならない。テンプレートの実引数に指定する非型テンプレートパラメータの値が異なれば、異なる個別のクラス定義になる。
例のarr1とarr2はLENが3と2が違うだけだが、それらは同じArrクラスではない。Arr<long, 3>とArr<long, 2>という異なるクラスである。
#include <iostream>
using namespace std;
template <typename T, int LEN>
class Arr {
public:
Arr() {
arrp = new T[LEN];
}
~Arr() { delete arrp;};
void set(T e, int i) {
arrp[i] = e;
}
T get(int i) {
return arrp[i];
}
private:
T* arrp;
};
int main()
{
Arr<long, 3> arr1;
arr1.set(10000L, 0);
arr1.set(20000L, 1);
cout << arr1.get(0) << endl;
cout << arr1.get(1) << endl;
Arr<long, 2> arr2;
arr2.set(10000L, 0);
cout << arr2.get(0) << endl;
}
using namespace std;
template <typename T, int LEN>
class Arr {
public:
Arr() {
arrp = new T[LEN];
}
~Arr() { delete arrp;};
void set(T e, int i) {
arrp[i] = e;
}
T get(int i) {
return arrp[i];
}
private:
T* arrp;
};
int main()
{
Arr<long, 3> arr1;
arr1.set(10000L, 0);
arr1.set(20000L, 1);
cout << arr1.get(0) << endl;
cout << arr1.get(1) << endl;
Arr<long, 2> arr2;
arr2.set(10000L, 0);
cout << arr2.get(0) << endl;
}
C++ テンプレート | 2024.08.05 |
テンプレートパラメータの参照型 |
テンプレートパラメータの指定型を参照で実装してもよい。テンプレートパラメータに与える側で、
zero<struct Buf&>
のようにする方法もある。
zero<struct Buf&>
のようにする方法もある。
#include <cstdio>
#include <cstring>
struct Buf {
char d[10];
};
template <typename T>
void zero(T& p)
{
memset((void *)&p, 0, sizeof(T));
}
int main()
{
struct Buf buf;
zero<struct Buf>(buf);
return 0;
}
#include <cstring>
struct Buf {
char d[10];
};
template <typename T>
void zero(T& p)
{
memset((void *)&p, 0, sizeof(T));
}
int main()
{
struct Buf buf;
zero<struct Buf>(buf);
return 0;
}