TENKAIKEN Storehouse

Contents ♪

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;
}
C++ テンプレート 2024.08.05
クラスメンバ関数個別のテンプレート
クラスのメンバ関数をテンプレートで定義する。
この例のmainで、<long>の部分を<short>などにしてもArrのset()とget()がそれに合わせて定義される。
コンストラクタもテンプレートで、
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;
}
C++ テンプレート 2024.08.05
クラステンプレート
クラスをテンプレートで定義する「クラステンプレート」。
メンバ関数の実装の関数宣言部分は、
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;
}
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;
}
C++ テンプレート 2024.08.05
クラステンプレートの継承
クラステンプレートをスーパークラスにした継承。
クラステンプレートの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";
    }
}
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;
}
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
}
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
}
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;
}
C++ テンプレート 2024.08.05
テンプレートパラメータの参照型
テンプレートパラメータの指定型を参照で実装してもよい。テンプレートパラメータに与える側で、
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;
}