【问题标题】:Implementing meta-function zip in c++11在 C++11 中实现元函数 zip
【发布时间】:2011-11-24 06:42:39
【问题描述】:

我实际上是想看看是否可以得到一个最小的库来支持我从 boost::fusion 中使用的极少数操作。

这是我目前所拥有的......

template < typename... Types >
struct typelist
{
};

template < template < typename... > class F, typename... Args >
struct apply
{
  typedef typename F < Args... >::type type;
};

template < typename, template < typename... > class >
struct foreach;

template < typename... Types, template < typename Arg > class F >
struct foreach < typelist < Types... >, F >
{
  typedef typelist < typename apply < F, Types >::type... > type; 
};

由于元函数foreach 的实现很简单,我认为zip 也很简单。显然,情况并非如此。

template < typename... >
struct zip;

template < typename...  Types0, typename... Types1 >
struct zip < typelist < Types0... >, typelist < Types1... > >
{
  typedef typelist < typelist < Types0, Types1 >... > type;
};

如何将此zip 元函数推广到任意数量的类型列表?我们这里需要的似乎是参数包的参数包。我不知道该怎么做。

编辑 1:

is_equal的实现...

template < std::size_t... Nn >
struct is_equal;

template < std::size_t N0, std::size_t N1, std::size_t... Nn >
struct is_equal < N0, N1, Nn... >
: and_ <
    typename is_equal < N0, N1 >::type
  , typename is_equal < N1, Nn... >::type
  >::type
{
};

template < std::size_t M, std::size_t N >
struct is_equal < M, N > : std::false_type
{
  typedef std::false_type type;
};

template < std::size_t N >
struct is_equal < N, N > : std::true_type
{
  typedef std::true_type type;
};

我认为zip 也可以采用类似的方法...尚未尝试使用zip,但我回家后会这样做。

编辑 2:

这是我最终认为看起来更优雅的东西。这基本上是 Vaughn Cato 方法的一种变体。

namespace impl
{

template < typename Initial, template < typename, typename > class F, typename... Types >
struct foldl;

template < typename Initial, template < typename, typename > class F, typename First, typename... Rest >
struct foldl < Initial, F, First, Rest... >
{
  typedef typename foldl < typename F < Initial, First >::type, F, Rest... >::type type;
};

template < typename Final, template < typename, typename > class F >
struct foldl < Final, F >
{
  typedef Final type;
};

template < typename Type, typename TypeList >
struct cons;

template < typename Type, typename... Types >
struct cons < Type, typelist < Types... > >
{
  typedef typelist < Types..., Type > type;
};

template < typename, typename >
struct zip_accumulator;

template < typename... Types0, typename... Types1 >
struct zip_accumulator < typelist < Types0... >, typelist < Types1... > >
{
  typedef typelist < typename cons < Types1, Types0 >::type... > type;
};

template < typename... Types0 >
struct zip_accumulator < typelist <>, typelist < Types0... > >
{
  typedef typelist < typelist < Types0 >... > type;
};

template < typename... TypeLists >
struct zip
{
  typedef typename foldl < typelist <>, zip_accumulator, TypeLists... >::type type;
};

}

template < typename... TypeLists >
struct zip
{
  static_assert(and_ < typename is_type_list < TypeLists >... >::value, "All parameters must be type lists for zip");
  static_assert(is_equal < TypeLists::length... >::value, "Length of all parameter type lists must be same for zip");
  typedef typename impl::zip < TypeLists... >::type type;
};

template < typename... TypeLists >
struct zip < typelist < TypeLists... > > : zip < TypeLists... >
{
};

这会将zip 视为fold 操作。

【问题讨论】:

  • 真的是拉链吗?我认为 zip 看起来更像 typelist&lt;pair&lt;Types0, Types1&gt;...&gt;(可以推广到除 pair 之外的任何元函数)。
  • @LucDanton:你是对的。我已经更正了zip 的实现。 typelist 是广义的 pair 元函数。但是如何传递任意数量的参数包呢?
  • @GregoryPakosz:如果不用样板也能做到,那就太棒了。
  • zip&lt;typelist&lt;one, three, five&gt;, typelist&lt;two, four, six&gt;&gt;::typetypelist&lt;typelist&lt;one, three, five&gt;, typelist&lt;two, four, six&gt;&gt;。这与其说是zip,不如说是cons,有点,不是真的。我建议的是typelist&lt;typelist&lt;Types0, Types1&gt;...&gt;,它产生typelist&lt;typelist&lt;one, two&gt;, typelist&lt;three, four&gt;, typelist&lt;five, six&gt;&gt;
  • 不应该 apply (= map) 生成带有模板参数 &lt;F&lt;T1&gt;, F&lt;T2&gt;, F&lt;TRest&gt;… &gt; 的模板,而不是像现在这样的 F&lt;T1, T2, TRest…&gt;

标签: c++ c++11 variadic-templates


【解决方案1】:

这是我发现的最短的实现:

template <typename...> struct typelist { };   
template <typename A,typename B> struct prepend;
template <typename A,typename B> struct joincols;
template <typename...> struct zip;    

template <typename A,typename... B>
struct prepend<A,typelist<B...> > {
  typedef typelist<A,B...> type;
};

template <>
struct joincols<typelist<>,typelist<> > {
  typedef typelist<> type;
};

template <typename A,typename... B>
struct joincols<typelist<A,B...>,typelist<> > {
  typedef typename
    prepend<
      typelist<A>,
      typename joincols<typelist<B...>,typelist<> >::type
    >::type type;
};

template <typename A,typename... B,typename C,typename... D>
struct joincols<typelist<A,B...>,typelist<C,D...> > {
  typedef typename
    prepend<
      typename prepend<A,C>::type,
      typename joincols<typelist<B...>,typelist<D...> >::type
    >::type type;
};

template <>
struct zip<> {
  typedef typelist<> type;
};

template <typename A,typename... B>
struct zip<A,B...> {
  typedef typename joincols<A,typename zip<B...>::type>::type type;
};

【讨论】:

  • +1 用于避免 enable_ifgcc_workaround 并完全依赖模板模式匹配。我想我看到了一些由此产生的模式。我正在添加与zip 一起使用的is_equal 元函数的实现,以确保其所有参数typelists 具有相同的长度。
  • 你能解释一下什么是 zip 元功能吗?而这个question 也与 zip 元功能有关?谢谢
  • @Mr.Anubis:这是一个很好的答案:stackoverflow.com/questions/1115563/…
【解决方案2】:

似乎可以使用成熟的列表(即头、尾和 cons 操作)和递归。用 GCC 4.7 的快照测试,所有 std 的东西都来自 &lt;type_traits&gt;

struct nil {};

template<typename T>
struct is_nil: std::is_same<T, nil> {};

template<typename... T>
struct and_: std::true_type {};

template<typename First, typename... Rest>
struct and_<First, Rest...>
: std::integral_constant<
    bool
    , First::value && and_<Rest...>::value
> {};

template<typename T>
struct not_
: std::integral_constant<bool, !T::value> {};

template<typename... T>
struct typelist;

template<typename First, typename Second, typename... Rest>
struct typelist<First, Second, Rest...> {
    typedef First head;
    typedef typelist<Second, Rest...> tail;
};

template<typename Last>
struct typelist<Last> {
    typedef Last head;
    typedef nil tail;
};

template<typename T, typename List>
struct cons;

template<typename T, typename... Ts>
struct cons<T, typelist<Ts...>> {
    typedef typelist<T, Ts...> type;
};

// workaround for:
// sorry, unimplemented: cannot expand '...' into a fixed-length argument list
template<template<typename...> class Template, typename... T>
struct gcc_workaround {
    typedef Template<T...> type;
};

namespace detail {

template<typename Sfinae, typename... Lists>
struct zip;

template<typename... Lists>
struct zip<
    typename std::enable_if<and_<is_nil<typename Lists::tail>...>::value>::type
    , Lists...
> {
    typedef typelist<typelist<typename Lists::head...>> type;
};

template<typename... Lists>
struct zip<
    typename std::enable_if<and_<not_<is_nil<typename Lists::tail>>...>::value>::type
    , Lists...
> {
    typedef typename cons<
        typelist<typename Lists::head...>
        , typename gcc_workaround<zip, void, typename Lists::tail...>::type::type
    >::type type;
};

} // detail

template<typename... Lists>
struct zip: detail::zip<void, Lists...> {};

您可能希望为所有这些添加错误检查(我正在考虑目前只是作为不完整类型留下的无效实例化)。坦率地说,考虑到我花了很多时间来解决这个问题,我建议坚持使用 Boost.MPL。像懒惰的评估(我不需要做 SFINAE 的东西)这样的事情是一个福音,我不喜欢重新发明它们。再加上 C++11 让您获得两全其美的一天。


我忘了提到 Boost.MPL 还具有通用性的优点。它可以在满足其序列概念之一的任何类型上工作(也可以非侵入性地适应预先存在的类型),而您强制使用typelist

【讨论】:

  • typelist 类型作为一个缺点列表来完成工作。我想知道是否可以不诉诸于此。
  • @zrb 你可以做template&lt;template&lt;typename...&gt; class... Lists, typename... T&gt; struct foo&lt;Lists&lt;T...&gt;...&gt; { ...,但这只会匹配具有相同元素的列表。
  • 被严重低估的答案,这个
猜你喜欢
  • 2011-12-12
  • 2018-11-25
  • 2013-07-27
  • 1970-01-01
  • 2014-08-19
  • 2012-08-06
  • 1970-01-01
  • 2015-06-17
  • 1970-01-01
相关资源
最近更新 更多