目次
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 はp1とp2 が指すオブジェクトの内容を内部的に比較するのではなく、p1とp2ポインターのアドレスを比較します。期待を満たさず、エラーが発生します。
この時点で、テンプレートを特殊化する必要があります。つまり、元のテンプレート クラスに基づく特殊な型の特殊な実装メソッドです
。テンプレートの特化は、関数テンプレートの特化
と
クラス テンプレートの特化に分かれます。
2.2 関数テンプレートの特殊化
関数テンプレートの特殊化手順:
- まず基本的な関数テンプレートを用意する必要があります
- キーワードテンプレートの後には、空の山かっこ<>が続きます。
- 関数名の後には、特殊化する型を指定する 1 対の山括弧が続きます。
- 関数パラメータリスト: テンプレート関数の基本パラメータ型と完全に同じである必要がありますが、異なる場合、コンパイラが奇妙なエラーを報告する可能性があります。
// 函数模板 -- 参数匹配
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.
テンプレートのコンパイル エラーが発生すると、エラー メッセージが非常に煩雑になり、エラーを特定するのが困難になります。