【问题标题】:How does boost implement lists of types as "options" for classes?boost 如何将类型列表实现为类的“选项”?
【发布时间】:2012-04-08 16:27:16
【问题描述】:

如果不使用示例来表达这个问题有点困难,所以我会直截了当。

作为一个基本示例,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;

这真是太酷了……他们到底是如何使这项工作适用于所有不同的组合的。如果我两次传递相同类型的选项会发生什么?对于boost::instrusive::list,可能的选项是:

  • base_hook&lt;class Hook&gt; / member_hook&lt;class T, class Hook, Hook T::* PtrToMember&gt; / value_traits&lt;class ValueTraits&gt;:所有这些选项都指定了要插入到列表中的类型 T 和钩子之间的关系(因为我们可以在同一个 T 类型中有多个钩子)。 member_hook 将在稍后解释,而 value_traits 将在具有自定义 ValueTraits 的容器部分中解释。如果没有指定选项,容器将被配置为使用带有默认标签的基本钩子。为钩子配置的一些选项(指针的类型、链接模式等)将传播到容器。

  • constant_time_size&lt;bool Enabled&gt;:指定容器是否需要恒定时间大小()函数。这将指示侵入式容器存储一个附加成员以跟踪容器的当前大小。默认情况下,激活恒定时间大小。

  • size_type&lt;bool Enabled&gt;:指定可以容纳容器大小的类型。此类型将是 list.size() 返回的类型,如果请求了 constant_time_size,则将是存储在侵入式容器中的类型。用户通常不需要更改此类型,但某些容器的 size_type 可能与 std::size_t 不同(例如,类似 STL 的容器使用其分配器定义的 size_type)。 Boost.Intrusive 可用于实现指定大小类型的此类容器。默认类型是 std::size_t。

我喜欢这个概念,因为它允许编译类型的行为定义。但正如您可以想象的那样,各种组合可能会变得复杂。我猜想通过一些魔法,他们将选项标准化为一个简单的结构,可以用于真实的数据结构。但这只是猜测工作:-P

【问题讨论】:

  • 我认为使用模板参数推导并不难。
  • @Cosyn:这与模板参数推导无关。我想你可能误解了这个问题。
  • 看Boost.Parameter;这就是 Boost.Intrusive 用来创建命名参数的方法。

标签: c++ templates boost


【解决方案1】:

我在尝试策略库设计时已经这样做了几次。

我使用的核心思想是策略被标记(通过内部 typedef,类似于迭代器的 iterator_category),然后我定义了一个类,它将简单地遍历列表并为一个类别提取一个给定的策略,如果两个策略引用同一类别,则会引发编译错误。比如:

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_ 只需根据谓词在两种类型之间进行选择,组合子写为:

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),然后在需要时在类中重复实际类型。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-12-27
    • 2014-06-10
    • 1970-01-01
    • 2014-07-27
    • 2012-11-24
    • 1970-01-01
    相关资源
    最近更新 更多