1

例を使わずにこの質問を表現するのは少し難しいので、私はちょっとそれを理解します。

基本的な例として、boost::intrusive::listいくつかの興味深いテンプレートがあり、それらがどのように機能するかを正確に理解するのに苦労しています。クラス仕様は次のようになります。

template<typename T, class... Options> 
class list {
   ...
};

私の焦点はOptionsパラメータです。手始めに、それは可変個引数です。これは、言語でサポートされているため、c++11では「些細なこと」です。そして確かにc++03でシミュレートするのに十分簡単です(最大10個のパラメーターを持つことができ、すべてデフォルトのトークン値があります)。

これが私の質問です:

Options、任意の数の「オプション」タイプを任意の順序で取ることができます。例えば:

typedef list<Foo, constant_time_size<false> > FooList;

また

//This option will configure "list" to use the member hook
typedef member_hook<Foo, list_member_hook<>, &Foo::hook_> MemberHookOption;

//This list will use the member hook
typedef list<Foo, MemberHookOption> FooList;

これは本当にクールです...彼らはすべての異なる組み合わせでこれをどのように機能させていますか?同じタイプのオプションを2回渡すとどうなりますか?の場合boost::instrusive::list、可能なオプションは次のとおりです。

  • base_hook<class Hook> / member_hook<class T, class Hook, Hook T::* PtrToMember> / value_traits<class ValueTraits>:これらのオプションはすべて、リストに挿入されるタイプTとフックの間の関係を指定します(同じTタイプに複数のフックを含めることができるため)。member_hookについては少し後で説明し、value_traitsについてはContainers withcustomValueTraitsセクションで説明します。オプションが指定されていない場合、コンテナーはデフォルトのタグでベースフックを使用するように構成されます。フック用に構成されたいくつかのオプション(ポインターのタイプ、リンク・モードなど)は、コンテナーに伝搬されます。

  • constant_time_size<bool Enabled>:コンテナに定数時間size()関数が必要かどうかを指定します。これにより、侵入型コンテナに、コンテナの現在のサイズを追跡するための追加のメンバーを格納するように指示されます。デフォルトでは、一定時間サイズがアクティブになっています。

  • size_type<bool Enabled>:コンテナのサイズを保持できるタイプを指定します。この型は、list.size()によって返される型であり、constant_time_sizeが要求された場合は侵入型コンテナーに格納される型になります。通常、ユーザーはこのタイプを変更する必要はありませんが、一部のコンテナーはstd :: size_tとは異なるsize_typeを持つことができます(たとえば、STLのようなコンテナーはアロケーターによって定義されたsize_typeを使用します)。Boost.Intrusiveを使用して、サイズのタイプを指定するこのようなコンテナーを実装できます。デフォルトでは、タイプはstd::size_tです。

型の動作の定義をコンパイルできるので、この概念が好きです。しかし、ご想像のとおり、さまざまな組み合わせが複雑になる可能性があります。いくつかの魔法を通して、オプションを実際のデータ構造に使用できる単純な構造体に正規化すると推測しています。しかし、それは単なる推測の仕事です:-P

4

1 に答える 1

1

ポリシーベースの設計を実験するときに、これを数回行いました。

私が使用した中心的なアイデアは、ポリシーにタグが付けられていることでした(イテレーターの場合と同様に、内部のtypedefを介してiterator_category)。次に、リストをたどってカテゴリの特定のポリシーを抽出するクラスを定義し、次の場合にコンパイルエラーを引き起こします。 2つのポリシーが同じカテゴリを参照しました。何かのようなもの:

template <typename Tag, typename Opt, typename... Options>
struct CategoryExtractorImpl {
  typedef typename if_<
            same_type<typename Opt::Tag, Tag>,
            Opt,
            void
  >::type Lhs;
  typedef typename CategoryExtractorImpl<Tag, Options...>::type Rhs;
  typedef typename Combinator<Lhs,Rhs>::type type;
};

述語if_に基づいて2つのタイプから単純に選択し、コンビネータは次のように記述されます。

template <typename, typename> struct Combinator;
template <typename L> struct Combinator<L, void> { typedef L type; };
template <typename R> struct Combinator<void, R> { typedef R type; };
template <> struct Combinator<void, void> { typedef void type; };

もちろん、ポリシーがまったく提供されていない場合に備えて、デフォルトも提供する必要があります。

template <typename L, typename R> struct ForgivingCombinator { typedef L type; };
template <typename R> struct ForgivingCombinator<void, R> { typedef R type; };

そして最後に、次のようになります。

template <typename Default, typename... Options>
struct CategoryExtractor {
  typedef typename ForgivingCombinator<
      typename CategoryExtactorImpl<typename Default::Tag, Options...>::type
      Default
  >::type type;
};

これを使用すると、すべてのカテゴリを簡単に抽出できます。

template <typename... Options>
class List {
  typedef typename CategoryExtractor<DefaultPolicyX, Options...>::type PolicyX;
  typedef typename CategoryExtractor<DefaultPolicyY, Options...>::type PolicyY;
  ...
};

もちろん、一般的なポリシーベースの設計では、(EBOをトリガーするために)それらからプライベートに継承し、必要に応じてクラス内で実際の型を繰り返すため、おそらくもう少し冗長になります。

于 2012-04-08T16:51:19.570 に答える