【问题标题】:Variadic template unrolling to std::tuple可变参数模板展开到 std::tuple
【发布时间】:2016-01-13 18:05:46
【问题描述】:

我有一个过滤器类,它接受两个模板参数,输入数量和输出数量。

template<int Ins, int Outs>
class Filter
{
    // implementation
};

有时我需要串联多个过滤器,所以我想将它们包装在一个类中

template<int... args>
class Chain
{
};

这样当我使用链条时

Chain<5, 10, 25, 15> chain;

它将 args 展开成一个元组,最终在 Chain 类中得到类似的东西

std::tuple<Filter<5, 10>, Fiter<10, 25>, Filter<25, 15>> filters;

这样的事情可能吗?我对这些概念还很陌生,无法理解。

【问题讨论】:

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


    【解决方案1】:

    我们可以在三行中做到这一点,并且没有递归:

    template<int... args>
    struct Chain
    {
        // put args... into a constexpr array for indexing
        static constexpr int my_args[] = {args...};
    
        // undefined helper function that computes the desired type in the return type
        // For Is... = 0, 1, ..., N-2, Filter<my_args[Is], my_args[Is+1]>...
        // expands to Filter<my_args[0], my_args[1]>,
        //            Filter<my_args[1], my_args[2]>, ...,
        //            Filter<my_args[N-2], my_args[N-1]>
    
        template<size_t... Is>
        static std::tuple<Filter<my_args[Is], my_args[Is+1]>...>
                    helper(std::index_sequence<Is...>);
    
        // and the result
        using tuple_type = decltype(helper(std::make_index_sequence<sizeof...(args) - 1>()));
    };
    

    Demo.

    【讨论】:

    • 很好的解决方案,虽然我更喜欢像this one这样更干净的演示
    【解决方案2】:

    我们可以通过一些递归模板魔术来做到这一点:

    //helper class template which will handle the recursion
    template <int... Args>
    struct FiltersFor;
    
    //a helper to get the type of concatenating two tuples
    template <typename Tuple1, typename Tuple2>
    using tuple_cat_t = decltype(std::tuple_cat(std::declval<Tuple1>(),
                                                std::declval<Tuple2>())); 
    
    //pop off two ints from the pack, recurse
    template <int Ins, int Outs, int... Others>
    struct FiltersFor<Ins,Outs,Others...>
    {
        //the type of concatenating a tuple of Filter<Ins,Outs> with the tuple from recursion
        using type = tuple_cat_t<std::tuple<Filter<Ins,Outs>>, 
                                 typename FiltersFor<Outs,Others...>::type>;    
    };
    
    //base case, 1 int left
    template <int Dummy>
    struct FiltersFor<Dummy>
    {
        using type = std::tuple<>;
    };
    
    //for completeness
    template <>
    struct FiltersFor<>
    {
        using type = std::tuple<>;
    };
    
    //our front-end struct
    template<int... args>
    using Chain = typename FiltersFor<args...>::type;
    

    或者,我们可以摆脱单 int 和无 int 版本,并像这样定义主模板:

    template <int... Args>
    struct FiltersFor
    {
        using type = std::tuple<>;
    };
    

    现在我们可以这样测试:

    static_assert(std::is_same<Chain<1,2,3,4>, std::tuple<Filter<1,2>,Filter<2,3>,Filter<3,4>>>::value, "wat");
    static_assert(std::is_same<Chain<1,2>, std::tuple<Filter<1,2>>>::value, "wat");
    static_assert(std::is_same<Chain<>, std::tuple<>>::value, "wat");
    

    Demo

    【讨论】:

    • 对于Chain&lt;1,2,3,4&gt;,输出应该是std::tuple&lt;Filter&lt;1,2&gt;,Filter&lt;2,3&gt;,Filter&lt;3,4&gt;&gt;,而不是std::tuple&lt;Filter&lt;1,2&gt;,Filter&lt;3,4&gt;&gt;
    • @buttifulbuttefly 绝对是,但我认为明确不同的案例会对 OP 有所帮助,因为他是“这些概念的新手”。不过,我会将其添加到我的答案中,谢谢。
    • 我最终为这种特殊情况使用了另一种解决方案,但这对于理解模板元编程时的思维过程非常有用,谢谢!
    【解决方案3】:

    我已经遇到过类似的问题,最终得到了一个过滤器类的运算符*,它接受一个Filter&lt;Ins1, Ins&gt; 对象并构建一个Filter&lt;Ins1, Outs&gt;

    template<int Ins, int Outs>
    class Filter
    {
        template <int Ins1>
        Filter<Ins1, Outs> operator*(const Filter<Ins1, Ins> &rhs) const
        {
        // implementation
        }
    };
    

    现在的问题是:您的过滤器是做什么的?组合是否可能(也许我对在我的上下文中过滤器是一个函数而在我的情况下 operator* 是函数组合这一事实有偏见)

    【讨论】:

      猜你喜欢
      • 2020-04-07
      • 2016-03-15
      • 1970-01-01
      • 1970-01-01
      • 2012-04-09
      • 1970-01-01
      • 2020-03-19
      • 2019-03-23
      • 1970-01-01
      相关资源
      最近更新 更多