C ++テンプレート特化のコツ

画像 テンプレートの特殊化は、C ++言語の「複雑な」機能の1つであり、主にライブラリを作成するときに使用されます。 残念ながら、テンプレートの特殊化の一部の機能は、この言語の人気のある本ではあまり公開されていません。 さらに、テンプレート専用の公式ISO言語標準の53ページでさえ、混chaとした詳細を記述しており、「自分で推測する-それは明らかです」に多くを残しています。 カットの下で、テンプレートの特殊化の基本原則を明確に述べ、魔法の呪文の構築にこれらの原則をどのように使用できるかを示しました。





ハローワールド



テンプレートの使用にどのように慣れていますか? templateキーワードを使用してから、山かっこでテンプレートパラメーターの名前を入力 、その後にタイプと名前を続けます 。 パラメーターについては、タイプ(typename)または値(たとえば、int)も示します。 テンプレート自体の型は、クラス、構造体(構造体も一般にクラスです)、または関数(bool foo()など)です。 たとえば、最も単純なテンプレートクラス「A」は次のように定義できます。

画像

しばらくして、クラスがすべてのタイプで同じように動作し、intのようなトリッキーなクラスでは異なる動作をするようにします。 質問をでたらめに、スペシャライゼーションを作成します。アナウンスと同じように見えますが 、山括弧でテンプレートパラメータを指定するのではなく、名前の後にテンプレートの特定の引数を示します。



テンプレート<>クラスA <int> {};  //ここでintはテンプレート引数です


完了、intの特別な実装のメソッドとフィールドを作成できます。 この特殊化は通常、 完全 (完全特殊化または明示的特殊化)と呼ばれます。 ほとんどの実用的なタスクでは、これ以上は必要ありません。 そして、必要に応じて...



専用テンプレートは新しいテンプレートです。



ISO C ++標準を注意深く読むと、興味深いステートメントを見つけることができます。特殊なテンプレートクラスを作成することにより、 新しいテンプレートクラス (14.5.4.3)を作成します。 これにより何が得られますか? 特化したテンプレートクラスには、フィールドまたは型宣言が特化したテンプレートクラスにないメソッドを含めることができます。 テンプレートクラスのメソッドを特定の専門分野でのみ機能させたい場合に便利です。この専門分野でのみメソッドを宣言するだけで十分です。残りはコンパイラが行います。

専門化方法のみ

特殊なテンプレートには、独自のテンプレートパラメータがあります。



悪魔は細部にいることが知られています。 特殊なテンプレートクラスが完全に新しく完全に分離されたクラスであるという事実は確かに興味深いですが、これにはあまり魔法がありません。 そして、魔法は重要な結果をもたらしません-それが別個のテンプレートクラスである場合、非特殊化されたテンプレートクラスに関係しない別個のパラメーターを持つことができます (パラメーターは、角括弧で囲まれた後のパラメーターです)。 たとえば、次のように:



テンプレート<typename S、typename U>クラスA <int> {};


ただし、コンパイラそのようなコードだけをコンパイルしません -特殊なテンプレートクラスでは禁止されている新しいテンプレートパラメータSおよびUは使用しません(そして、特殊なコンパイラは、既に宣言されている名前と同じ名前 'A'を持つため、このクラスを理解しますテンプレートクラス)。 コンパイラーは、「明示的な特殊化では部分的な特殊化構文を使用しています。代わりにテンプレート<>を使用してください」という特別なエラーも表示します。 何も言うことがない場合は、テンプレート<>を使用する必要があります。 次に、なぜ特別なテンプレートクラスで新しいパラメーターを使用できるのですか? 答えは奇妙です-特殊化引数を指定するため(引数は山括弧で囲まれたクラス名の後に来るものです)。 つまり、テンプレートクラスを特殊化する代わりに、新しいパラメーターを使用して単純で直感的な方法でテンプレートクラスを特殊化できます



テンプレート<typename S、typename U>クラスA <std :: map <S、U >> {};


このような奇妙なレコードはコンパイルされます。 また、結果のテンプレートクラスをstd :: mapで使用する場合、キータイプstd :: mapが新しいテンプレートSのパラメーターとして使用でき、値タイプstd :: mapがUとして使用される特殊化が使用されます。



このようなテンプレートの特殊化(パラメーターの新しいリストが指定され、これらのパラメーターを介して特殊化の引数が指定される)は、部分特殊化と呼ばれます。 なぜ「部分的」なのですか? どうやら、元々はすべての引数ではなくテンプレートを特化する構文として考えられていたからです。 2つのパラメーターを持つテンプレートクラスがそのうちの1つだけに特化する例(特化は、最初の引数Tがintとして指定された場合に機能します。この場合、2番目の引数は任意です。このため、新しいパラメーターUが部分特化に導入され、リストされます専門化のための引数):



テンプレート<typename T、typename S>クラスB {};
テンプレート<typename U>クラスB <int、U> {};


部分的特殊化の魔法の効果



上記のテンプレート特化の2つのプロパティのうち、興味深い結果がいくつかあります。 たとえば、部分的な特殊化を使用する場合、新しいテンプレートパラメーターを導入し、それらを通して特殊な引数を記述することにより、複合型を単純なものに分割することができます。 以下の例では、テンプレート引数が関数ポインター型の場合、特殊なテンプレートクラスAが使用されます。 この場合、テンプレートSおよびUの新しいパラメーターを使用して、この関数の戻り値の型とその引数の型を取得できます。



テンプレート<typename S、typename U>クラスA <S(*)(U)> {};


また、特殊なテンプレートでtypedefまたはstatic const intを宣言する場合(これが新しいテンプレートであるという事実を利用して)、それを使用して型から必要な情報を抽出できます。 たとえば、テンプレートクラスを使用してオブジェクトを保存し、渡されたオブジェクトのサイズを取得するか、ポインターの場合は0を取得します。 2行で:



 template <typename T> struct Get {const static intサイズ= sizeof(T);  };
 template <typename S> struct Get <S *> {const static intサイズ= 0;  };

 Get <int> ::サイズ// 4など
 Get <int *> ::サイズ// 0-ポインターが見つかりました:)


このタイプの魔法は、主にライブラリで使用されます:stl、boost、lokiなど。 もちろん、高レベルのプログラミングを使用する場合、そのようなトリックを使用するのは少しトリッキーです-配列のサイズを取得するために誰もがデザインを覚えていると思います:)。 しかし、ライブラリでは、部分的な特殊化により、デリゲート、イベント、複雑なコンテナ、およびその他の非常に必要で有用なものを比較的簡単に実装できます。



同僚、あなたが間違いを見つけた場合(そして、残念ながら、グルではありません-私は間違っている可能性があります)、または上記に対する批判、質問、または追加がある場合、私はコメントしてうれしいです。



更新:約束された続編はこちら




All Articles