【C++学習】テンプレート上級

目次

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

2. テンプレートの特化

2.1 コンセプト

2.2 関数テンプレートの特殊化

2.3 クラステンプレートの特殊化

2.3.1全特化

2.3.2 部分的な専門化

2.3.3 クラステンプレートの特化適用例

3. テンプレート分離コンパイル

3.1 個別編集とは何ですか?

3.2 テンプレートの個別コンパイル

3.3 解決策

4. テンプレートの概要


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

テンプレート パラメータの分類仮パラメータは非タイプの仮パラメータに含まれます
        。 タイプ パラメータ: テンプレート パラメータのリストに表示され、その後にクラスやタイプ名などのパラメータのタイプ名が続きます。
        非型パラメータとは、クラス(関数)テンプレートのパラメータとして定数を使用することで、クラス(関数)テンプレート内で定数として使用することができます。

namespace bite
{
    // 定义一个模板类型的静态数组
    template<class T, size_t N = 10>
    class array
    {
    public:
        T& operator[](size_t index){return _array[index];}
        const T& operator[](size_t index)const{return _array[index];}
 
        size_t size()const{return _size;}
        bool empty()const{return 0 == _size;}
 
    private:
        T _array[N];
        size_t _size;
    };
}

注:
1. 浮動小数点数、クラス オブジェクト、および文字列は、型以外のテンプレート パラメーターとして使用できません。

2. 非型テンプレートパラメータの結果はコンパイル時に確認する必要があります。

2. テンプレートの特化

2.1 コンセプト

        通常、テンプレートを使用すると、型に依存しないコードを実装できますが、一部の特殊な型では、間違った 結果 特殊な処理が必要になる場合があります。たとえば、特に小なり比較用の関数テンプレートを実装するなどです。
// 函数模板 -- 参数匹配
template<class T>
bool Less(T left, T right)
{
    return left < right;
}
int main()
{
    cout << Less(1, 2) << endl; // 可以比较,结果正确
    Date d1(2022, 7, 7);
    Date d2(2022, 7, 8);
    cout << Less(d1, d2) << endl; // 可以比较,结果正确
    Date* p1 = &d1;
    Date* p2 = &d2;
    cout << Less(p1, p2) << endl; // 可以比较,结果错误
    return 0;
}
ほとんどの場合、Less は通常どおり比較できますが、特殊なシナリオでは誤った結果が得られることが         わかります。上記の例では、p1 が指す d1 はp2が指すd2オブジェクトより明らかに小さい です が、Less はp1p2 が指すオブジェクトの内容を内部的に比較するのではなくp1p2ポインターのアドレスを比較します。期待を満たさず、エラーが発生します。
        この時点で、テンプレートを特殊化する必要があります。つまり、元のテンプレート クラスに基づく特殊な型の特殊な実装メソッドです テンプレートの特化は、関数テンプレートの特化 クラス テンプレートの特化に分かれます。

2.2 関数テンプレートの特殊化

関数テンプレートの特殊化手順:
  1. まず基本的な関数テンプレートを用意する必要があります
  2. キーワードテンプレートの後には、空の山かっこ<>が続きます。
  3. 関数名の後には、特殊化する型を指定する 1 対の山括弧が続きます。
  4. 関数パラメータリストテンプレート関数の基本パラメータ型と完全に同じである必要がありますが、異なる場合、コンパイラが奇妙なエラーを報告する可能性があります。
// 函数模板 -- 参数匹配
template<class T>
bool Less(T left, T right)
{
    return left < right;
}
// 对Less函数模板进行特化
template<>
bool Less<Date*>(Date* left, Date* right)
{
    return *left < *right;
}
int main()
{
    cout << Less(1, 2) << endl;
    Date d1(2022, 7, 7);
    Date d2(2022, 7, 8);
    cout << Less(d1, d2) << endl;
    Date* p1 = &d1;
    Date* p2 = &d2;
    cout << Less(p1, p2) << endl; // 调用特化之后的版本,而不走模板生成了
    return 0;
}

        注: 一般に、関数テンプレートで処理できない型、または正しく処理されない型が見つかった場合、実装を簡略化するために、関数は通常直接指定されます。

bool Less(Date* left, Date* right)
{
    return *left < *right;
}
        この種の実装はシンプルかつ明確であり、コードは非常に読みやすく、記述も簡単です。複雑なパラメータ型を持つ一部の関数テンプレートでは特殊化が行われるため、関数テンプレートの特殊化は推奨されません。

2.3 クラステンプレートの特殊化

2.3.1全特化
完全な特殊化とは、テンプレート パラメーター リスト内のすべてのパラメーターが決定されることを意味します。
template<class T1, class T2>
class Data
{
public:
    Data() {cout<<"Data<T1, T2>" <<endl;}
private:
    T1 _d1;
    T2 _d2;
};
template<>
class Data<int, char>
{
public:
    Data() {cout<<"Data<int, char>" <<endl;}
private:
    int _d1;
    char _d2;
};
void TestVector()
{
    Data<int, int> d1;
    Data<int, char> d2;
}
2.3.2 部分的な専門化
部分的特殊化: テンプレート パラメーターの設計をさらに制限する特殊化。たとえば、次のテンプレート クラスの場合:
template<class T1, class T2>
class Data
{
public:
    Data() {cout<<"Data<T1, T2>" <<endl;}
private:
    T1 _d1;
    T2 _d2;
};
部分特殊化には次の 2 つの式があります。

1. 部分特殊化:テンプレートパラメータクラステーブルのパラメータの一部を特殊化します。

// 将第二个参数特化为int
template <class T1>
class Data<T1, int>
{
public:
    Data() {cout<<"Data<T1, int>" <<endl;}
private:
    T1 _d1;
    int _d2;
};
2. パラメータのさらなる制限: 部分的な特殊化は、単に一部のパラメータを特殊化することを意味するのではなく、テンプレート パラメータをさらに制限するように設計された特殊化されたバージョンを意味します。
//两个参数偏特化为指针类型
template <typename T1, typename T2>
class Data <T1*, T2*>
{ 
public:
    Data() { cout<<"Data<T1*, T2*>" <<endl; }
private:
    T1 _d1;
    T2 _d2;
};
//两个参数偏特化为引用类型
template <typename T1, typename T2>
class Data <T1&, T2&>
{
public:
    Data(const T1& d1, const T2& d2)
        : _d1(d1)
        , _d2(d2)
    {
        cout<<"Data<T1&, T2&>" <<endl;
    }
 
private:
    const T1 & _d1;
    const T2 & _d2; 
};
void test2 () 
{
    Data<double , int> d1; // 调用特化的int版本
    Data<int , double> d2; // 调用基础的模板 
    Data<int *, int*> d3; // 调用特化的指针版本
    Data<int&, int&> d4(1, 2); // 调用特化的指针版本
}
2.3.3クラステンプレートの特化適用例
次のクラス テンプレートがあります。これは、より小さいことに基づく比較にあまり具体的には使用されません。
#include<vector>
#include <algorithm>
template<class T>
struct Less
{
    bool operator()(const T& x, const T& y) const
    {
        return x < y;
    }
};
int main()
{
    Date d1(2022, 7, 7);
    Date d2(2022, 7, 6);
    Date d3(2022, 7, 8);
    vector<Date> v1;
    v1.push_back(d1);
    v1.push_back(d2);
    v1.push_back(d3);
    // 可以直接排序,结果是日期升序
    sort(v1.begin(), v1.end(), Less<Date>());
    vector<Date*> v2;
    v2.push_back(&d1);
    v2.push_back(&d2);
    v2.push_back(&d3);
 
    // 可以直接排序,结果错误日期还不是升序,而v2中放的地址是升序
    // 此处需要在排序过程中,让sort比较v2中存放地址指向的日期对象
    // 但是走Less模板,sort在排序时实际比较的是v2中指针的地址,因此无法达到预期
    sort(v2.begin(), v2.end(), Less<Date*>());
    return 0;
}
        上記のプログラムの結果を観察すると、日付オブジェクトを直接並べ替えることができ、結果が正しいことがわかりました。ただし、ソート対象の要素がポインタの場合、結果が正しくない可能性があります。 なぜなら、sort は最終的にLessテンプレートのメソッド に従って比較するため 、ポインターが指す空間の内容ではなく、ポインターのみを比較します。このとき、クラス バージョンの特殊化を使用して上記の問題に対処できます。
// 对Less类模板按照指针方式特化
template<>
struct Less<Date*>
{
    bool operator()(Date* x, Date* y) const
    {
        return *x < *y;
    }
};
特殊化後、上記のコードを実行すると正しい結果が得られます。

3. テンプレート分離コンパイル

3.1個別編集とは何ですか?

        プログラム (プロジェクト) は複数のソース ファイルで実装され、各ソース ファイルを個別にコンパイルしてオブジェクト ファイルを生成し、最後にすべてのオブジェクト ファイルをリンクして 1 つの実行ファイルを作成するプロセスを個別コンパイル モードと呼びます。

3.2 テンプレートの個別コンパイル

次のシナリオがある場合、テンプレートの宣言と定義は分離され、ヘッダー ファイルで宣言され、ソース ファイルで完成されます。
// a.h
template<class T>
T Add(const T& left, const T& right);
// a.cpp
template<class T>
T Add(const T& left, const T& right)
{
    return left + right;
}

// main.cpp
#include"a.h"
int main()
{
    Add(1, 2);
    Add(1.0, 2.0);
 
    return 0;
}

3.3 解決策

1. ファイル "xxx.hpp " または xxx.hに宣言と定義を置く こともできます。 これを使用することをお勧めします。
2. テンプレートが定義されている場所での明示的なインスタンス化 この方法は実用的ではないため、お勧めできません。

4. テンプレートの概要

【アドバンテージ】
1. テンプレートはコードを再利用し、リソースを節約し、より高速な反復開発を可能にするため、 C++ 標準テンプレート ライブラリ (STL) が誕生しました。
2. コードの柔軟性の向上
【欠陥】
1. テンプレートによりコードが肥大化し、コンパイル時間が長くなる可能性があります。
2. テンプレートのコンパイル エラーが発生すると、エラー メッセージが非常に煩雑になり、エラーを特定するのが困難になります。

おすすめ

転載: blog.csdn.net/weixin_44906102/article/details/132395626