[C++] 高度なテンプレート: 非型テンプレート パラメーターとテンプレートの特殊化とテンプレートの個別のコンパイル


前回のブログでは、ジェネリック プログラミングとテンプレート[C++] テンプレート の概念に初めて触れましたが 、そのときは基本的な使い方しか説明していませんでした。

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

テンプレート パラメータは、型パラメータ非型パラメータに分けられます

型パラメーター: テンプレート パラメーター リストに表示されるパラメーター型の名前。class または typename の後に続きます。

非型パラメータ: クラス(関数)テンプレートのパラメータとして定数を使用することで、そのパラメータはクラス(関数)テンプレートで定数として使用できます。

注: 1. この定数の型は整数ファミリのみであり、浮動小数点数、クラス オブジェクト、および文字列は非型テンプレート パラメータとして許可されません。

2.非型テンプレート パラメータはコンパイル ノードで確認する必要があります

例えば:

テンプレート型の静的配列を定義するとします。

//按照C语言的方法,需要#define一个容量
#define N 10;
template<class T>
class Array
{
    
    
public:
	//...
private:
	T _a[N];
};
//但是在C++中,就可以使用非类型模板参数
template<class T, size_t N = 10>//这里也可以像函数参数一样给缺省值
class Array
{
    
    
public:
	//...
private:
	T _a[N];
};

補足:配列

C++11 では、新しいコンテナーが追加されました: 配列

画像-20230429211100873

このコンテナーは、下部にある静的配列と同じであり、スタックに格納される固定サイズの順次コンテナーです配列の使用法に関するドキュメント

画像-20230429211338755

これらは配列の一部のインターフェースであり、静的配列であるため、push_back などの操作はサポートされていません。

では、なぜ配列クラスが表示されるのでしょうか。配列が登場する前は、配列はうまく使われていなかったのですか? これは、C 言語の配列範囲外の処理がスポット チェックであり、それほど厳密ではないためです. 配列の実装は、C 言語の静的配列を置き換えるために使用され、配列範囲外の検査に使用されます.チェックが強化されました。さらに、静的配列もコンテナーでカプセル化されます。これにより、C++ のカプセル化が具現化され、プログラム全体の可読性、抽象化、および互換性が向上します。

2. テンプレートの専門化

1. コンセプト

通常、テンプレートは型に依存しないコードを実装するために使用できますが、一部の特殊な型では、間違った結果が得られる場合があり、特別な処理が必要になります。

void Test()
{
    
    
	cout << Less(1, 2) << endl; // 可以比较,结果正确
	Date d1(2023, 4, 30);
	Date d2(2023, 5, 1);
	cout << Less(d1, d2) << endl; // 可以比较,结果正确
	Date* p1 = new Date(2023, 4, 30);
	Date* p2 = new Date(2023, 5, 1);
	cout << Less(p1, p2) << endl; // 可以比较,结果错误
	cout << p1 << endl;
	cout << p2 << endl;
}

画像-20230430001302472

ほとんどの場合、Less は正常に動作することがわかりますが、一部の特殊なシナリオでは、比較のロジックがわずかに異なるため、最終的に正しい答えを出すことができなくなります。特殊な場合の処理​​は、テンプレートを特殊化する必要があります。つまり、元のテンプレートに基づいて特殊なタイプの特殊な処理を実行する必要があります。

テンプレート特化は関数テンプレート特化とクラステンプレート特化に分けられる

2. 関数テンプレートの特化

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

  1. 最初に基本的な関数テンプレートが必要です
  2. キーワード template の後に、一対の空の山括弧 <>が続きます
  3. 関数名の後に一対の山括弧が続き、特殊化する型を指定します
  4. 関数のパラメーター リストは、関数テンプレートの基本的なパラメーターの型とまったく同じでなければなりません。そうしないと、一連の奇妙なエラーが報告されます。

上記の例では、次のテンプレートの特殊化を行うことができます

template<class T>
bool Less(T left, T right)
{
    
    
	cout << "Less(T left, T right)" << endl;//这里为了方便观察,我们对函数调用进行一下标识
	return left < right;
}
//对Less函数模板进行特化
template<>
bool Less<Date*>(Date* left, Date* right)
{
    
    
	cout << "Less<Date*>(Date* left, Date* right)" << endl;
	return *left < *right;
}

画像-20230430004509068

ただし、この状況では、関数のオーバーロードの方法を直接使用して解決できます。1 つ以上の仮パラメーターをオーバーロードするだけです。したがって、一般に、関数テンプレートが処理できない型または正しく処理されない型に遭遇した場合、簡単にするために、関数は通常直接与えられます (関数のオーバーロード)

template<class T>
bool Less(T left, T right)
{
    
    
	cout << "Less(T left, T right)" << endl;
	return left < right;
}
//函数重载
bool Less(Date* left, Date* right)
{
    
    
	cout << "Less(Date* left, Date* right)" << endl;
	return *left < *right;
}

画像-20230430004915643

上記のように、関数のオーバーロードを使用する方法はより明確で簡潔であるため、一般的に関数テンプレートの特殊化の使用はまれであり、一般的なケースでは、関数テンプレートの特殊化の使用は推奨されません

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

クラス テンプレートの特殊化は、関数テンプレートの特殊化に似ています

  1. 最初に基本的な関数テンプレートが必要です
  2. キーワード template の後に、一対の空の山括弧 <>が続きます
  3. 関数名の後に一対の山括弧が続き、特殊化する型を指定します

クラス テンプレートの特殊化は、完全な特殊化部分的な特殊化に分けられます

1. 全特化

名前が示すように、完全な特殊化は、テンプレート内のすべてのパラメーターを特殊化することです

template<class T1, class T2>
class Data
{
    
    
public:
	Data()
	{
    
    
		cout << "Data<T1, T2>" << endl;
	}
};
template<>
class Data<int, char>
{
    
    
public:
	Data()
	{
    
    
		cout << "Data<int, char>" << endl;
	}
};
void Test2()
{
    
    
	Data<int, int> d1;
	Data<int, char> d2;
}

画像-20230430011837964

2.部分特化

部分的な特殊化: クラス テンプレートのパラメーターをさらに制限する特殊化。部分特殊化には 2 つの形式があります。

  • 部分的な特殊化: クラス テンプレート パラメーター テーブルの一部のパラメーターを特殊化します。
  • パラメーターのさらなる制限: テンプレート パラメーターに対するさらなる条件付き制限のために設計された特別なバージョン
//部分特化:将第二个参数特化为int
template<class T1>
class Data<T1, int>
{
    
    
public:
	Data()
	{
    
    
		cout << "Data<T1, int>" << endl;
	}
};
//参数限制:将两个参数偏特化为指针类型
template<class T1, class T2>
class Data<T1*, T2*>
{
    
    
public:
	Data()
	{
    
    
		cout << "Data<T1*, T2*>" << endl;
	}
};
//参数限制:将两个参数偏特化为引用类型
template<class T1, class T2>
class Data<T1&, T2&>
{
    
    
public:
	Data()
	{
    
    
		cout << "Data<T1&, T2&>" << endl;
	}
};
void Test3()
{
    
    
	Data<int, double> d1;//调用基础的类模板
	Data<double, int> d2;//调用特化int版本
	Data<int*, int*> d3;//调用特化的指针版本
	Data<double&, int&> d4;//调用特化的引用版本
}

画像-20230430013337089

特殊化の本質は、コンパイラ パラメータの一致原理を反映することです。つまり、既製品があれば既製品を使用し、そうでなければ半完成品を使用し、どちらも存在しない場合にのみ、テンプレートをインスタンス化します。

3. テンプレート別編集

1. 分冊編集とは?

✅プログラム(プロジェクト)を複数のソースファイルでまとめて実装し、各ソースファイルを個別にコンパイルしてオブジェクトファイルを生成し、最終的にすべてのオブジェクトファイルをリンクして単一の実行ファイルを形成するプロセスを個別コンパイルモードと呼びます

2. 別のコンパイルを使用しないのはなぜですか?

✅C言語ではセパレートコンパイルを強く推奨していますが、ブロガーの以前のブログを読むと、STLのシミュレーションとテンプレートの使用以来、セパレートコンパイル方法が採用されていないことがわかります。どうしてこれなの?テンプレートは個別のコンパイルをサポートしていないため

次のシーンを見てみましょう

// 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;
}

画像-20230430014458465

3. 解決策

  • 実際には、宣言と定義をファイル「xxx.hpp」またはxxx.hに入れることができます。これはお勧めです。
  • テンプレート定義が明示的にインスタンス化される場所この方法は非現実的であり、推奨されません。

4. テンプレートのまとめ

テンプレートの出現により、プログラミング作業の複雑さが大幅に軽減されましたが、いくつかの欠点もあります

アドバンテージ:

  1. テンプレートはコードを再利用し、リソースを節約し、より迅速な反復開発を可能にします. C++ 標準テンプレート ライブラリ (STL) はこうして生成されます.
  2. コードの柔軟性の向上

欠点:

  1. テンプレートはコードの肥大化とコンパイル時間の延長につながる可能性があります

  2. テンプレートのコンパイル エラーが発生した場合、エラー メッセージが非常に煩雑で、エラーを特定するのが容易ではありません

おすすめ

転載: blog.csdn.net/weixin_63249832/article/details/130445696