テンプレートマジックの紹介

C ++のテンプレートは、メタプログラミングツールであり、コンパイル時のポリモーフィズムを実装します。 これは何ですか

これは、ポリモーフィックな振る舞いでコードを記述するときですが、振る舞い自体はコンパイル段階で決定されます。つまり、仮想関数のポリモーフィズムとは対照的に、結果のバイナリコードは既に一定の振る舞いをします。



なんで?



画像

美しさのためにパターンを使用します。 すべてのC ++開発者は、美しさが何であるかを知っています。美しさとは、コードがコンパクトで明確かつ高速 場合です



メタマジックと暗黙のインターフェイス



メトプログラムとは何ですか? metoprogramは、別のプログラムをもたらすプログラムです。 C ++の場合、コンパイラはメタプログラムの実行に関与し、結果はバイナリファイルになります。



画像



テンプレートが使用されるのは、メタプログラムを作成するためです。

パターンの多型は、仮想関数の多型とどのように違うのですか? クラスがクラス宣言で定義した明示的なインターフェイスを持っている場合、このタイプのプログラムオブジェクトでは、まさにこのインターフェイスに従って使用できます。 ただし、テンプレートには暗黙的なインターフェイス、つまり 型のオブジェクトを使用して、メタプログラムを構築するときにコンパイラが出力する型の暗黙的なインターフェイスを定義します



最初の呪文:マジッククラブ



画像



テンプレートを指定し、さまざまなテンプレートパラメーターに対して取得した型を確認します。



typedef char CHAR; int main() { B<int> b; B<char> c; B<unsigned char> uc; B<signed char> sc; B<CHAR> C; B<char, 1> c1; B<char, 2-1> c21; cout << "b=" << typeid(b).name() << endl; cout << "c=" << typeid(c).name() << endl; cout << "C=" << typeid(C).name() << endl; cout << "sc=" << typeid(sc).name() << endl; cout << "uc=" << typeid(uc).name() << endl; cout << "c1=" << typeid(c1).name() << endl; cout << "c21=" << typeid(c21).name() << endl; return 0; }
      
      





プログラム出力は、テンプレートのインスタンス化タイプが同等のタイプ(unsigned char&char)であっても異なることを示しています 。 さらに、これらはcharとCHARで同一です。 typedefは型を作成せず、別の名前を付けるだけです。 式1と2-1では同じです。 コンパイラは式を評価し、2-1の代わりに1を使用します。



これは、追加の問題なしにテンプレートに個別のコンパイルを使用できないことを意味します。

ああ
 #include <iostream> using namespace std; template <typename T> class A { public: void f(); };
      
      







main.cpp
 #include "export.h" int main() { A<int> a; af(); return 0; }
      
      







a.cpp
 #include "ah" template <typename T> void A<T>::f() { cout << "A<t>::f" << endl; } template class A<int>;
      
      







一般に、C ++標準にはこのためのエクスポートキーワードがありますが、この機能は実装するのが難しすぎるため、ほとんどのコンパイラにはありません。 それをサポートするコンパイラはありますが、移植可能なコードで使用することはお勧めしません。



クラスに加えて、関数テンプレートがあります:

 template<typename T> T func(T t, T d) { cout << "func" << endl; }; int main() { func('1', 2); }
      
      





コンパイラーがパラメーターのタイプからテンプレートパラメーターのタイプを推測できる場合は、そうするため、コードで指定する必要はありません。 そうでない場合は、解決関数を決定できます。



  inline int func(char c, int i) { return func<int>(c, i); };
      
      





彼女はオーバーヘッドを負いません。



専門化は新しいレベルです。



画像



通常、テンプレートを使用してユニバーサルコードを記述しますが、場合によってはパフォーマンスが低下することがあります。 問題を解決するために、テンプレートの特殊化という特別な呪文があります。 特殊化とは、特定のタイプまたはタイプのクラスを使用したテンプレートの再定義です。



 #include <iostream> using namespace std; template<typename T> T func(T t) { cout << "func" << endl; }; template<typename T> T * func(T *t) { cout << "func with pointer!" << endl; }; int main() { func(2); int i = 2; func(&i); }
      
      





コンパイラーは、最適な特殊化自体を選択します。この例では、タイプポインター型クラスです。



不吉な魔法:再帰



特殊化とテンプレートでテンプレートを使用できるという事実は、非常に興味深い機会を提供します-コンパイル時間の再帰です。



画像



最も単純で最も一般的な例は、一連の自然数の和などの級数または多項式の計算です。



 #include <iostream> using namespace std; template <int i> int func() { return func<i-1>()+i; }; template <> int func<0>() { return 0; }; int main () { cout << func<12>() << endl; return 0; };
      
      





私たちは見て...それは動作します! かっこいい 反復回数を500に増やします。



 cout << func<500>() << endl;
      
      





プログラムの実行時間は一定ですが、コンパイルに時間がかかります! 奇跡!



雷雨が必要な場合はヤギを使わないでください



いくつかのポイントがあります。



画像



最大の再帰の深さは実装によってデフォルトで制限されます。新しいgccの場合は900で、古いバージョンの場合は小さくなります。 パラメータ

 $ g++ recursion.cpp -ftemplate-depth=666666666
      
      





この制限を取り除きます。



第二の落とし穴-バグレポートを待たないでください。 量を階乗に変更します。



 int func() { return func<i-1>() * i; }; template <> int func<0>() { return 1; }; ... cout << func<500>() << endl;
      
      





単一の警告ではなく、誤った結果が得られます。



3番目の点は明らかです。テンプレートのほとんど同一のインスタンスを作成しすぎると、生産性が向上する代わりにバイナリコードが増加します



強力な古代の呪文



継承の魔法とテンプレートの魔法を組み合わせることは可能ですか?



画像



古代人はこのためにCRTPスペルを使用します。 アイデアは単純です。非仮想継承を適用​​し、相続人のタイプを親のタイプに明示的にキャストすることにより、多態的な動作を提供します。 使用例を見てみましょう:



 template<typename Filtrator> class FiltratorImpl { inline void find_message(...) { Filtrator* filtrator = static_cast<Filtrator* >(this); … filtrator->find_and_read_message(info, collection); } }; ... class CIFSFiltrator : public FiltratorImpl<CIFSFiltrator> { ... inline void find_and_read_message(PacketInfo& info) {...} ... }; class RPCFiltrator : public FiltratorImpl<RPCFiltrator> { ... inline void find_and_read_message(PacketInfo& info) {...} ... };
      
      





多相的な振る舞いを持つ継承されたインラインメソッドを取得ます! これがクールではないと言う人は、永遠に私の敵です。



古代人は、親コンストラクタに次のようなものを追加することも勧めました。



 static_assert(std::is_member_function_pointer<decltype(&Filtrator::find_and_read_message)>::value)
      
      





強力な呪文によって目覚めた悪魔は、それらを引き起こした魔術師を傷つけることができませんでした。



画像



古くはないものの、もっと多くの秘密のテクニックがあります。 素早い出会い/ *地獄で* /そして、古代人の力があなたと共に来るかもしれません。



All Articles