【问题标题】:Most elegant way to split a C++ TypeList拆分 C++ TypeList 的最优雅方式
【发布时间】:2018-04-01 19:18:21
【问题描述】:

在“现代”C++ 中,我有一个类型列表:

template <typename... T> struct TypeList {};

我想根据谓词拆分类型列表,例如std::is_floating_point。更准确地说,我的完整工作示例是:

#include <iostream>
#include <type_traits>

template <typename... T> struct TypeList {};

// SplitTypeList<> implementation defined at the end of this post...

template <typename T>
void printType()
{
  std::cout << "\n" << __PRETTY_FUNCTION__;
}

int main()
{
  struct A
  {
  };

  using typeList = TypeList<int, double, float, A, int>;

  using splited_typeList = SplitTypeList<std::is_floating_point, typeList>;

  using float_typeList = splited_typeList::predicate_is_true_typeList_type;
  using other_typeList = splited_typeList::predicate_is_false_typeList_type;

  printType<float_typeList>();
  printType<other_typeList>();
}

打印:

g++ -std=c++17 typeList.cpp -o typeList; ./typeList

void printType() [with T = TypeList<double, float>]
void printType() [with T = TypeList<int, main()::A, int>]

我的问题:你知道一个可能的更短/更优雅只使用 C++(C++17 没问题)和 STL 的解决方案吗? (我不想使用像 Boost、Hana 这样的辅助库...)。

(我的动机:我不想错过一两行/超级优雅的解决方案,因为我将在其他地方广泛使用此功能)


我目前的实现是:

namespace Details
{
  template <template <typename> class PREDICATE,
            typename... TYPELIST_PREDICATE_IS_TRUE,
            typename... TYPELIST_PREDICATE_IS_FALSE>
  constexpr auto splitTypeList(TypeList<TYPELIST_PREDICATE_IS_TRUE...>,
                               TypeList<TYPELIST_PREDICATE_IS_FALSE...>,
                               TypeList<>)
  {
    return std::make_pair(TypeList<TYPELIST_PREDICATE_IS_TRUE...>(),
                          TypeList<TYPELIST_PREDICATE_IS_FALSE...>());
  }

  template <template <typename> class PREDICATE,
            typename... TYPELIST_PREDICATE_IS_TRUE,
            typename... TYPELIST_PREDICATE_IS_FALSE,
            typename T,
            typename... TAIL>
  constexpr auto splitTypeList(TypeList<TYPELIST_PREDICATE_IS_TRUE...>,
                               TypeList<TYPELIST_PREDICATE_IS_FALSE...>,
                               TypeList<T, TAIL...>)
  {
    if constexpr (PREDICATE<T>::value)
    {
      return splitTypeList<PREDICATE>(
          TypeList<TYPELIST_PREDICATE_IS_TRUE..., T>(),
          TypeList<TYPELIST_PREDICATE_IS_FALSE...>(),
          TypeList<TAIL...>());
    }
    else
    {
      return splitTypeList<PREDICATE>(
          TypeList<TYPELIST_PREDICATE_IS_TRUE...>(),
          TypeList<TYPELIST_PREDICATE_IS_FALSE..., T>(),
          TypeList<TAIL...>());
    }
  }

  template <template <typename> class PREDICATE, typename... T>
  constexpr auto splitTypeList(TypeList<T...>)
  {
    return splitTypeList<PREDICATE>(
        TypeList<>(), TypeList<>(), TypeList<T...>());
  }
}

template <template <typename> class PREDICATE, typename TYPELIST>
struct SplitTypeList;

template <template <typename> class PREDICATE, typename... TAIL>
struct SplitTypeList<PREDICATE, TypeList<TAIL...>>
{
  using pair_type = decltype(
      Details::splitTypeList<PREDICATE>(std::declval<TypeList<TAIL...>>()));
  using predicate_is_true_typeList_type = typename pair_type::first_type;
  using predicate_is_false_typeList_type = typename pair_type::second_type;
};

出于好奇,一个指向 TypeList 的历史指针(Andrei Alexandrescu,2002 年 2 月 1 日):http://www.drdobbs.com/generic-programmingtypelists-and-applica/184403813

【问题讨论】:

    标签: c++ c++11 metaprogramming c++17


    【解决方案1】:

    这样的事情可能会更简单/更短

    template< bool, template<typename> class, class... Vs >
    auto FilterImpl( TypeList<>, TypeList<Vs...> v ) { return v; }
    
    template< bool Include, template<typename> class P, class T, class... Ts, class... Vs >
    auto FilterImpl( TypeList<T,Ts...>, TypeList<Vs...> ) { return FilterImpl<Include,P>(
      TypeList<Ts...>{} ,
      std::conditional_t< Include == P<T>::value, TypeList<T,Vs...>, TypeList<Vs...> >{}
      ); }
    
    template <template <typename> class PREDICATE, typename TYPELIST>
    struct SplitTypeList
    {
      using predicate_is_true_typeList_type = decltype(FilterImpl<true,PREDICATE>( TYPELIST{}, TypeList<>{} ));
      using predicate_is_false_typeList_type = decltype(FilterImpl<false,PREDICATE>( TYPELIST{}, TypeList<>{} ));
    };
    

    【讨论】:

    • 非常好的解决方案(恕我直言)。但是为什么( Include &amp;&amp; P&lt;T&gt;::value ) || ( !Include &amp;&amp; !P&lt;T&gt;::value ) 而不仅仅是Include == P&lt;T&gt;::value 呢?
    • @max66 是的,这更简单,我只是在一个布尔的心情:) 编辑
    • 我知道...我只处于模板元编程的心情;但您的解决方案更简单、更短并且(恕我直言)非常优雅。
    • 顺便说一句,如果我没记错的话,标准特征不需要只采用一个模板参数......所以这些帖子中的所有代码都应该通过在谓词中添加尾随类型名来修复......嗯...
    • @MassimilianoJanes 不错的解决方案!我没有想到允许真正减少代码大小的布尔技巧。谢谢。
    【解决方案2】:

    我并不是说下面的方式更好或更优雅。

    这是不同的,这是我的方式。

    仅使用可变参数模板类的特化;没有功能。

    应该也适用于 C++11。

    希望这个例子有所帮助。

    #include <tuple>
    #include <type_traits>
    
    template <template <typename> class Pred, typename>
    struct PredValFirst : public std::false_type
     { };
    
    template <template <typename> class Pred,
              template <typename...> class C,
              typename T0, typename ... Ts>
    struct PredValFirst<Pred, C<T0, Ts...>> : public Pred<T0>
     { };
    
    
    template <template <typename> class Pred, typename List,
              typename = std::tuple<>, typename = std::tuple<>,
              bool = PredValFirst<Pred, List>::value>
    struct SplitTypeList;
    
    template <template <typename> class Pred, template <typename...> class C,
              typename T0, typename ... Ts, typename ... Tt, typename Lf>
    struct SplitTypeList<Pred, C<T0, Ts...>, std::tuple<Tt...>, Lf, true>
       : SplitTypeList<Pred, C<Ts...>, std::tuple<Tt..., T0>, Lf>
     { };
    
    template <template <typename> class Pred, template <typename...> class C,
              typename T0, typename ... Ts, typename Lt, typename ... Tf>
    struct SplitTypeList<Pred, C<T0, Ts...>, Lt, std::tuple<Tf...>, false>
       : SplitTypeList<Pred, C<Ts...>, Lt, std::tuple<Tf..., T0>>
     { };
    
    template <template <typename> class Pred, template <typename...> class C,
              typename ... Tt, typename ... Tf>
    struct SplitTypeList<Pred, C<>, std::tuple<Tt...>, std::tuple<Tf...>, false>
     {
       using types_true  = C<Tt...>;
       using types_false = C<Tf...>;
     };
    
    template <typename...>
    struct TypeList
     { };
    
    struct A
     { };
    
    int main ()
     {
       using typeList = TypeList<int, double, float, A, int>;
    
       using splited_typeList = SplitTypeList<std::is_floating_point, typeList>;
    
       using float_typeList = splited_typeList::types_true;
       using other_typeList = splited_typeList::types_false;
    
       static_assert( std::is_same<float_typeList,
                                   TypeList<double, float>>{}, "!");
       static_assert( std::is_same<other_typeList,
                                   TypeList<int, A, int>>{}, "!");
     }
    

    【讨论】:

      猜你喜欢
      • 2012-03-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-10-12
      • 2013-04-23
      • 1970-01-01
      • 2020-03-15
      相关资源
      最近更新 更多