C++ のテンプレート

目次

関数テンプレート

クラステンプレート 

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

テンプレートの特化 


関数テンプレート

テンプレートは汎用プログラミングの基礎であり、特定の型に依存しない方法でコードを記述します。

テンプレートは、ジェネリック クラスまたは関数を作成するための設計図または式です。イテレータやアルゴリズムなどのライブラリ コンテナは汎用プログラミングの例であり、どちらもテンプレートの概念を使用します。

すべてのコンテナには、 Vectorなどの単一の定義があり 、 Vector<int> や Vector<string>など、さまざまな種類のベクトルを定義できます 

関数テンプレートは関数ファミリーを表します。関数テンプレートは型とは関係がありません。使用時にパラメータ化され、実際のパラメータの型に従って関数の特定の型バージョンが生成されます。(型をパラメータ化します)

関数テンプレートの形式

テンプレート <タイプ名 T1,タイプ名 T2,タイプ名 T3...>

テンプレート<タイプ名 T>

void Swap( T& x, T& y)

{

        温度T = x;

        x=y;

        y= 温度;

}  

関数テンプレートの原理

これは関数そのものではなく、コンパイラーが使用方法に応じて特定の具体的な型の関数を生成するための型です。つまり、実際には、テンプレートは、私たちが行うべき繰り返しの処理をコンパイラーに渡すことになります。コンパイラーのコンパイル段階では、テンプレート関数を使用するために、コンパイラーは、渡される実際のパラメーターのタイプに応じて、呼び出しに対応するタイプの関数を推測して生成する必要があります。たとえば、int 型の関数テンプレートを使用する場合、コンパイラは実際のパラメーターの型を推測して T が double 型であると判断し、特に int 型を処理するコードを生成します。これは、キャラクタータイプ。

関数テンプレートのインスタンス化

関数テンプレートが異なる型のパラメーターとともに使用される場合、それは関数テンプレートのインスタンス化と呼ばれます。テンプレート パラメーターのインスタンス化は、暗黙的なインスタンス化明示的なインスタンス化に分けられます

暗黙的なインスタンス化: コンパイラに実際のパラメータに基づいてテンプレート パラメータの実際の型を推定させます。

テンプレート <クラス T>

Add(const T& x, const T& y) {

        x+y を返します。

}

int main() {

        int a1 = 100、a2 = 200;

        二重 d1 = 100.0、d2 = 200.0;

        追加(a1, a2);

        Add(a1, d2);//このステートメントはコンパイルできません。コンパイル中に、コンパイラーがインスタンス化を確認したときに、そのパラメーターの型を推定し、実際のパラメーター a1 を通じて T を int に推定し、実際のパラメーター d1 を使用する必要があるためです。 T は double 型として推定されますが、テンプレート パラメーター リストには T が 1 つしかなく、コンパイラーは T を int 型と double 型のどちらとして決定すべきかを判断できず、エラーが報告されます。

現時点では、ユーザーは変換を強制するか、明示的なインスタンス化を使用することができます。

        Add(a, (int)d);//強制変換

        0を返します。

}

明示的なインスタンス化: 関数名の後の <> 内にテンプレート パラメータの実際の型を指定します 

int main() {

        int a = 10;

        ダブル b = 20.0;

        // 明示的なインスタンス化

        Add<int>(a, b);

        //vector<int> v

        0を返します。

}

 型が一致しない場合、コンパイラは暗黙的な型変換を実行しようとし、変換が失敗するとエラーを報告します。

テンプレートパラメータの一致原則 

非テンプレート関数は、同じ名前の関数テンプレートと共存でき、関数テンプレートをこの非テンプレート関数としてインスタンス化することもできます。

// int の特殊な加算関数

int Add(int x, int y) {

        x+y を返します。

}

// 一般的な加算関数

テンプレート <クラス T> 

Add(T x, T y) {

        x+y を返します。

}

void テスト() {

        Add(10, 20); // 非テンプレート関数と一致するため、コンパイラは特殊化を必要としません

        Add<int>(10, 20); // コンパイラに特化したバージョンの Add を呼び出します

}

非テンプレート関数と同名の関数テンプレートの場合、他の条件が同じ場合、非テンプレート関数が先に呼び出され、呼び出し時にテンプレートからインスタンスは生成されません。テンプレートがより適切に一致する関数を生成できる場合は、テンプレートを選択します。

// int の特殊な加算関数

int Add(int x, int y) {

        x+y を返します。

}

// 一般的な加算関数

テンプレート <クラス T1,クラス T2> 

Add(T x, T y) {

        x+y を返します。

}

void テスト() {

       Add(10, 20); // 非関数テンプレート型と完全に一致するため、関数テンプレートのインスタンス化は必要ありません

       Add(10, 20.0); // テンプレート関数はより一致するバージョンを生成でき、コンパイラは実際のパラメータに基づいてより一致する Add 関数を生成します。

}

テンプレート関数では自動型変換はできませんが、通常の関数では自動型変換が可能です。

クラステンプレート 

クラステンプレートの定義形式

テンプレート<クラス T1、クラス T2、 ... クラス Tn >

クラス クラステンプレート名 {

        // クラスメンバー定義

};

クラステンプレート内の関数がクラス外で定義されている場合、テンプレートパラメータリストを追加する必要があります

テンプレートクラス<T>

スタック{

公共 :

        //クラス内で宣言され、クラス外で定義される

         ~スタック();

}

テンプレート<クラス T>

stack<T> ::~stack() {

        //

}  

クラステンプレートのインスタンス化 

クラス テンプレートのインスタンス化は、関数テンプレートのインスタンス化とは異なります。クラス テンプレートのインスタンス化は、クラス テンプレート名の後に <> を付け、インスタンス化された型を <> 内に置く必要があります。クラス テンプレート名は実際のクラスではなく、インスタンス化されたものです。結果は次のようになります。本当のクラス。

// スタック クラス名、stack<int> は型です

        stack<int> ;

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

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

名前空間 leiyaling{

// テンプレート タイプの静的配列を定義します

テンプレート <class T,size_t N=20>

クラス配列 {

公共:

       //...

プライベート:

        T _配列[N];

        サイズ_t _サイズ;

};

  •  浮動小数点数、クラス オブジェクト、文字列は非型テンプレート パラメーターとして許可されません。
  • 非型テンプレート パラメーターには、コンパイル時に確認された結果が必要です。 

テンプレートの特化 

通常、テンプレートを使用して型に依存しないコードを実装できますが、一部の特殊な型では、誤った結果が得られる可能性があり、特別な処理が必要になります。たとえば、以下の比較に特別に使用される関数テンプレートを実装します。

// 関数テンプレート -- パラメータのマッチング

テンプレート<クラス T>

bool Less(T x, T y) {

        x< y を返します。

}

int main() {

        cout << Less(1, 2) << endl; // 比較可能、結果は正しい

        日付 d1(2022, 7, 7);

        日付 d2(2022, 7, 8);

        cout << Less(d1, d2) << endl; // 比較可能、結果は正しい

        日付* p1 = &d1;

        日付* p2 = &d2;

        cout << Less(p1, p2) << endl; // 比較はできますが、結果は間違っています

        0を返します。

}  

ほとんどの場合、Less は正常に比較できますが、特殊なシナリオでは誤った結果が得られることがわかります。上記の例では、p1 が指す d1 は、p2 が指す d2 オブジェクトよりも明らかに小さいですが、Less は内部的に p1 と p2 が指すオブジェクトの内容を比較するのではなく、p1 と p2 ポインターのアドレスを比較します。 、それは期待に応えられず、間違っています。

この時点で、テンプレートを特殊化する必要があります。つまり、元のテンプレート クラスに基づいて、特殊な型に特化した実装メソッドです。テンプレートの特化は関数テンプレートの特化クラステンプレートの特化に分かれます 

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

関数テンプレートの特殊化手順は次のとおりです。

  1. 最初に基本的な関数テンプレートが必要です
  2. キーワード テンプレートの後には、空の山かっこ <> が続きます。
  3. 関数名の後には、特殊化する型を指定する 1 対の山括弧が続きます。
  4. 関数パラメータテーブル: テンプレート関数の基本パラメータの型と完全に同じである必要があります。異なる場合、コンパイラが奇妙なエラーを報告する可能性があります。

テンプレート<クラス T>

bool Less(T x, T y) {

        x< y を返します。

}

// Less 関数テンプレートを特殊化する

テンプレート<>

bool Less <日付*> (日付* x, 日付* y) {

        戻り値 *x< *y;

}

int main() {

        cout << Less(1, 2) << endl;

        日付 d1(2022, 7, 7);

        日付 d2(2022, 7, 8);

        cout << Less(d1, d2) << endl;

        日付* p1 = &d1;

        日付* p2 = &d2;

        cout << Less(p1, p2) << endl; // テンプレート生成の代わりに特化したバージョンを呼び出す

        0を返します。

}

一般に、関数テンプレートで処理できない型、または正しく処理されない型に遭遇した場合、通常は単純化のために関数が直接与えられます。 

bool Less(日付* x, 日付* y) {

        戻り値 *x< *y;

}

この種の実装はシンプルかつ明確で、コードは非常に読みやすく、記述も簡単です。複雑なパラメーター型を持つ一部の関数テンプレートでは、特殊化中に特殊化が行われるため、関数テンプレートの特殊化は推奨されません。 

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

全特化

完全な特殊化は、テンプレート パラメーター リスト内のすべてのパラメーターを決定することです。

template<class T1, class T2>
class Data
{ public:     Data() { cout << "Data<T1, T2>" << endl; プライベート:     T1 _d1;     T2_d2; }; template<> class Data<int, char> { public:     Data() { cout << "Data<int, char>" << endl; プライベート:     int _d1;     文字_d2; }; void TestVector() {     Data<int, int> d1;     データ<int, char> d2; }



















部分的な専門化

部分的特殊化: テンプレート パラメーターの設計をさらに条件付きで制限する特殊化。

部分特化には 2 つのタイプがあります。

1.部分特化

テンプレート パラメーター クラス リスト内のパラメーターの一部を特殊化します。 

// 2 番目のパラメータを int
テンプレートとして特殊化します <class T1>
class Data<T1, int>
{         // };

2. パラメータのさらなる制限

部分的な特殊化は、一部のパラメーターの特殊化だけを指すのではなく、テンプレート パラメーターの条件をさらに制限するために設計された特殊化されたバージョンを指します。

//2 つのパラメータは部分的にポインタ型
テンプレートとして特化されています <typename T1, typename T2>
class Data <T1*, T2*>
{     // }; //2 つのパラメータは部分的に参照型テンプレートとして特化されています <typename T1, typename T2>クラス データ <T1&, T2&> {     // };







テンプレートの宣言と定義は分離をサポートしていないため、通常は宣言と定義をファイル "xxx.hpp" または xxx.h に入れます。 

テンプレートを使用する利点

  • テンプレートにはサイズを指定するための非型パラメータを含めることができ、そこから動的構造を作成できます。
  • テンプレートで最も重要なことは、テンプレートが型に依存しないことであり、これによりコードの再利用性が向上します。
  • テンプレート構文がサポートされている限り、テンプレート コードは移植可能です

テンプレートは実行時にデータ型をチェックせず、型のマクロ置換と同等の型安全性を保証しません。

おすすめ

転載: blog.csdn.net/m0_55752775/article/details/129035433