C ++可变参数模板类型过滤转换

时间:2018-04-18 16:39:37

标签: c++ templates metaprogramming

问题。

我是模板元编程的新手,不确定如何实现 元组上的类型过滤转换,在提供时生成类型 带有过滤描述。我相信以下代码段演示 我想要的行为。

enum : int {
  INCLUDE,
  EXCLUDE
};

template <int filter_val, class T>
struct filter {
};

int
main() {

  struct A {};
  struct B {};
  struct C {};

  typedef std::tuple<filter<INCLUDE, A>,
                     filter<EXCLUDE, B>,
                     filter<INCLUDE, C>> filters;

  typedef filter_all<filters>::type filtered;

  static_assert(std::is_same<filtered,
                             std::tuple<A, C>
                            >::value,
                ":(");

  return 0;
}

我尝试了什么

据我所知,你不能解包超过1个独立的可变参数模板,所以我想到的解决问题的方法是在递归模板专门化过程中维护两个元组,其中一个代表我们在递归中的位置,而另一个代表迄今为止的T'。

template <template <class ...> class, class ...Unfiltered_Ts>
struct filter_all {
private: 
  template <class Unfiltered_Ts_Tuple, class Included_Ts_Tuple>
  struct filter_all_impl;

  // CASE 1: Include T in the result
  template <
    template <int, class> class, int filter_val, class T, class ...Unfiltered_Ts_impl, // Unfiltered input
    template <class ...> class, class ...Included_Ts                                   // Result so far
  >
  struct filter_all_impl<std::tuple<filter<INCLUDE, T>,
                                    Unfiltered_Ts_impl...>,
                         std::tuple<Included_Ts...>> {
    typedef typename
      filter_all_impl<std::tuple<Unfiltered_Ts_impl...>,
                      std::tuple<Included_Ts..., T> // Append T to result
                     >::type type;
  };

  // CASE 2: Don't include T in the result
  template <
    template <int, class> class, int filter_val, class T, class ...Unfiltered_Ts_impl, // Unfiltered input
    template <class ...> class, class ...Included_Ts                                   // Result so far
  >
  struct filter_all_impl<std::tuple<filter<EXCLUDE, T>,
                                    Unfiltered_Ts_impl...>,
                         std::tuple<Included_Ts...>
                        > {
    typedef typename
      filter_all_impl<std::tuple<Unfiltered_Ts_impl...>,
                      std::tuple<Included_Ts...> // Don't append T to result
                     >::type type;
  };

  // CASE 3: Filtering finished - set the final type as the included T's
  template <
    template <int, class> class, int filter_val, class T, class ...Unfiltered_Ts_impl, 
    template <class ...> class, class ...Included_Ts
  >
  struct filter_all_impl<<>, // empty
                         std::tuple<Included_Ts...>
                        > {
    // Final step, set type as a tuple of all included Ts
    typedef std::tuple<Included_Ts...> type;
  };
public:
  typedef typename filter_all_impl<
    std::tuple<Unfiltered_Ts...>, // Initially contains all unfiltered Ts
    std::tuple<>                  // Initially empty filtered Ts which eventually becomes the return type
  >::type type;
};

我希望有一种更简单的方法来进行这种转换,但这是我到目前为止所得到的,但它远没有编译和抱怨模板专业化无效。任何指导都表示赞赏。

1 个答案:

答案 0 :(得分:6)

首先,定义一个pick<T>帮助器,为已排除的项返回空元组,另一个包含项的元组:

template <typename>
struct pick;

template <typename T>
struct pick<filter<INCLUDE, T>> { using type = std::tuple<T>; };

template <typename T>
struct pick<filter<EXCLUDE, T>> { using type = std::tuple<>; };

然后,根据std::tuple_cat

实施过滤逻辑
template <typename>
struct filter_all;

template <typename... Ts>
struct filter_all<std::tuple<Ts...>>
{   
    using type = decltype(std::tuple_cat(typename pick<Ts>::type{}...));
};

完成!

live example on wandbox.org

如果您可以修改filter的实施以展示constexpr bool,那么它可以更简单:

template <int filter_val, class T>
struct filter 
{
    static constexpr bool pick = filter_val == INCLUDE;
    using type = T;
};

template <typename T>
struct filter_all;

template <typename... Ts>
struct filter_all<std::tuple<Ts...>>
{   
    using type = decltype(std::tuple_cat(
        std::conditional_t<Ts::pick, 
            std::tuple<typename Ts::type>, std::tuple<>>{}...
    ));
};

live example on wandbox.org