【问题标题】:C++ variadic template type filtering transformationC++ 可变参数模板类型过滤转换
【发布时间】:2018-04-18 16:39:36
【问题描述】:

问题。

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

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;
};

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

【问题讨论】:

  • 希望我能两次支持这个问题。感谢您的明确解释、测试驱动程序以及您的尝试。

标签: c++ templates metaprogramming


【解决方案1】:

首先,定义一个pick&lt;T&gt;帮助器,它为排除的项目返回一个空元组,否则为一个包含该项目的元组

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

【讨论】:

  • 非常感谢!我很高兴我的烂摊子没有编译,因为这将更容易管理。尽管我想知道我是否在正确的道路上让它做正确的事,尽管它很丑......
  • T 可能不是默认可构造的(因此tuple&lt;T&gt;),因此您应该将pick&lt;Ts&gt;::type{} 替换为std::declval&lt;pick&lt;Ts&gt;::type&gt;
猜你喜欢
  • 2011-07-11
  • 2013-09-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-04-02
相关资源
最近更新 更多