テンプレート初期化リストの任意の順序

テンプレートを扱う多くの人は、次の状況に精通していると思います。 一連のテンプレートパラメータを持つ特定のテンプレートクラスがあります



struct deferred; struct deadline; struct disable; template<class T, class Deferred = disable, class Deadline = disable> struct some_container
      
      





ある種の遅延タスク実行インターフェイス(アクティブキュー)とします。 そして、遅延実行と期限を追加することにより、機能を拡張したいと考えています。 ただし、これらすべてをすぐに有効にしたくはありませんが、必要に応じて必要な構成を組み立てることができます。 このようなテンプレート(多くのパラメーターがあり、ほとんどすべてが既にデフォルト値を持っている)の問題は、最後のパラメーターを再定義するために、その前にあるすべてのものを示す必要があることです。



 typedef some_container<int, disable, deadline> deadline_container; <source>    <source lang=cpp> typedef some_container<int, deadline> deadline_container;
      
      





さらに良いのは、割り当ての順序も問題ではなく、次の2つは同等であるということです。



 typedef some_container<int, deferred, deadline> full_container1; typedef some_container<int, deadline, deferred> full_container1;
      
      





しかし、2つのパラメーターを変更するとすぐに、予期したものとはまったく異なるものになることをよく知っています(これは、順序を示すことが重要でない場合のタプルではありません)

私たちのタイプとユーザーの間にレイヤーを追加することで、これらすべてを達成できると多くの人がすでに考えていると思います。 テンプレートパラメータが2つしかない場合は、はい、3つが既に困難で、4つが5つ追加されている場合は、書き込みがありません。 また、原則として、新しいパラメーターを追加すると、以前のすべてのスペシャライゼーションがやり直されます(スペシャライゼーションではテンプレートパラメーターの数を増やすことはできませんが、減らすことしかできないため)。

興味があるなら、カットをお願いします、これを達成する方法をお見せします



しかし、最初に、少しのタール。 テンプレートタイプは、ユーザーが独自のタイプを含むさまざまなタイプでパラメーター化できるため、優れています。 私が示したい方法では、テンプレートを任意の型に特化することはできません。 つまり たとえば、deadline_superタイプをdeadlineタイプに相当するものとして定義し、それをテンプレートの特殊化に置き換えることができます



 typedef some_container<int, disable, deadline_super>
      
      





ただし、テンプレートを任意の形式に特化できるメカニズムでこのタイプを使用することはできません。 おそらくこれが唯一の重大な制限です。 この側面を考えると、このメカニズムは、拡張可能または多態的ではなく、モジュール式コンポーネントを記述するときに使用するのが便利であることが明らかになります。

実装全体は、 mpl :: setなどのブーストコンポーネントに基づいています。 boost :: mplについてはあまり説明しません 、mpl :: setを使用すると、std :: setに似たコンテナを作成できますが、コンパイル段階で型で構成されます。

最初に必要なのは、必要な型の型のリストを確認し、デフォルト値(struct disable)を返す方法です



 template <class Plugin, class IsSet> struct get_plugin_impl; template <class Plugin> struct get_plugin_impl<Plugin, boost::mpl::false_> { typedef disable_plugin type; }; template <class Plugin> struct get_plugin_impl<Plugin, boost::mpl::true_> { typedef Plugin type; }; template <class List, class Plugin> struct get_plugin { typedef typename get_plugin_impl<Plugin, typename boost::mpl::has_key<List, Plugin>::type>::type type; };
      
      





これにより、必要なタイプが指定されているかどうかをタイプのリストから確認し、指定されていない場合はデフォルト値を使用できます。 ご覧のとおり、このコードは最終構造のパラメーター化に関与する1つのタイプ(およびこの特定のタイプを使用していないというレポート)のみに依存するため、新しいテンプレートタイプを追加してもこのコードは変更されません。

次のステップでは、最終テンプレートで使用されるすべてのタイプを定義します



 template <class List> struct get_plugins { typedef typename get_plugin<List, deferred_plugin>::type deferred; typedef typename get_plugin<List, deadline_plugin>::type deadline; };
      
      





これは、新しいテンプレートパラメータを追加するときに変更される場所です。 しかし、これは非常に簡単で、2 ^ nの組み合わせをリストする必要はありません。

次に、最終的なテンプレートタイプとユーザーの間にレイヤーを導入します



 template<class T, class P1 = disable_plugin, class P2 = disable_plugin> struct container { typedef boost::mpl::set<P1, P2> plugin_list; typedef get_plugins<plugin_list> plugs; typedef typename plugs::deferred deferred; typedef typename plugs::deadline deadline; typedef some_container<T, deferred, deadline> type; };
      
      





つまり、テンプレートパラメータを指定する数と順序から抽象化することができます。 実際、異なる数の指定されたテンプレートパラメータとその順序について記述する必要があるすべての(2 ^ n + K)特殊化を組み合わせています。



テンプレートタイプに戻り、その仕組みを説明します



 #define static_type(name) \ static const std::string& type() \ { \ static std::string type_(name); \ return type_; \ } \ struct deferred_plugin { static_type("deferred"); }; struct deadline_plugin { static_type("deadline"); }; struct disable_plugin { static_type("disable"); }; template<class T, class Deferred, class Deadline> struct some_container { static const std::string& type() { static std::string type_("some_container<" + Deferred::type() + ", " + Deadline::type() + ">"); return type_; } };
      
      





使用する



  cout << container<int>::type::type() << std::endl; cout << container<int, deadline_plugin>::type::type() << std::endl; cout << container<int, deferred_plugin>::type::type() << std::endl; cout << container<int, deferred_plugin, deadline_plugin>::type::type() << std::endl; cout << container<int, deadline_plugin, deferred_plugin>::type::type() << std::endl;
      
      





排気



some_container <無効化、無効化>

some_container <無効、期限>

some_container <遅延、無効化>

some_container <遅延、期​​限>

some_container <遅延、期​​限>



テンプレートのインスタンス化時にタイプが指定された順序に関係なく、特殊化の順序は正しい形式で保存されることに注意してください。



それだけです。これが誰かがコードをより美しく正確にするのに役立つことを願っています。

ソース: git



All Articles