【问题标题】:Variadic templates: unfold arguments in groups可变参数模板:分组展开参数
【发布时间】:2020-03-19 23:06:31
【问题描述】:

我有一个带有两个参数的函数:

template <typename T1, typename T2>
void foo(T1 arg1, T2 arg2)
{ std::cout << arg1 << " + " << arg2 << '\n'; }

还有一个应该成对转发其参数的可变参数:

template <typename... Args>
void bar(Args&&... args) {
    static_assert(sizeof...(Args) % 2 == 0);

    ( foo( std::forward<Args>(args), std::forward<Args>(args) ), ... );
    // ^ Sends each argument twice, not in pairs
}

我希望bar(1,2,3,4) 致电foo(1,2)foo(3,4)

有没有办法做到这一点?

【问题讨论】:

  • 同一个args转发两次很危险

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


【解决方案1】:

你可以通过重载来完成它。

template <typename T1, typename T2>
void bar(T1&& arg1, T2&& arg2) {
    foo( std::forward<T1>(arg1), std::forward<T2>(arg2) ); // (until) sends (the last) two arguments to foo
}

template <typename T1, typename T2, typename... Args>
void bar(T1&& arg1, T2&& arg2, Args&&... args) {
    foo( std::forward<T1>(arg1), std::forward<T2>(arg2) ); // sends the 1st two arguments to foo
    bar( std::forward<Args>(args)... );                    // call bar with remaining elements recursively
}

LIVE


请注意,当使用 0 或奇数参数调用 bar 时,如果使用上述最小 sn-p,您将收到 no matching function 错误。如果您想通过static_assert 获得更清晰的编译信息,您可以从snippet 开始。

【讨论】:

    【解决方案2】:

    使用if constexpr的简单递归:

    // print as many pairs as we can
    template<class T, class U, class... Args>
    void foo(T t, U u, Args&&... args)
    {
        std::cout << t << " + " << u << "\n";
        if constexpr(sizeof...(Args) > 0 && sizeof...(Args) % 2 == 0)
            foo(std::forward<Args>(args)...);
    }
    
    template<class... Args>
    void bar(Args&&... args)
    {
        static_assert(sizeof...(Args) % 2 == 0);
        foo(std::forward<Args>(args)...);
    }
    

    这样称呼它:

    bar(1, 2, 3, 4);
    

    Demo

    我想说songyanyao's answer 是 C++17 之前的标准。之后,if constexpr 允许我们将逻辑移动到函数体中,而不是使用重载技巧。

    【讨论】:

    • songyanya 的版本很容易扩展,因此它也将它应用的函数作为参数。在我看来,这非常好,因为它允许我们多次应用这种模式,而不必每次都编写逻辑。您的答案是否有允许相同的版本?
    • @n314159: 类似this?
    • 没错!谢谢你。我个人更喜欢这个,因为(除了我已经说过的)它将应用逻辑与其应用的功能分开。
    【解决方案3】:

    n-ary 仿函数的 C++17 泛化:

    namespace impl
    {
        template<std::size_t k, class Fn, class Tuple, std::size_t... js>
        void unfold_nk(Fn fn, Tuple&& tuple, std::index_sequence<js...>) {
            fn(std::get<k + js>(std::forward<Tuple>(tuple))...);
        }
    
        template<std::size_t n, class Fn, class Tuple, std::size_t... is>
        void unfold_n(Fn fn, Tuple&& tuple, std::index_sequence<is...>) {
            (unfold_nk<n * is>(fn, std::forward<Tuple>(tuple), 
                std::make_index_sequence<n>{}), ...);
        }
    }
    
    template<std::size_t n, class Fn, typename... Args>
    void unfold(Fn fn, Args&&... args) {
        static_assert(sizeof...(Args) % n == 0);
        impl::unfold_n<n>(fn, std::forward_as_tuple(std::forward<Args>(args)...), 
            std::make_index_sequence<sizeof...(Args) / n>{});
    }
    
    int main() {
        auto fn = [](auto... args) { 
            (std::cout << ... << args) << ' ';
        };
    
        unfold<2>(fn, 1, 2, 3, 4, 5, 6);   // Output: 12 34 56
        unfold<3>(fn, 1, 2, 3, 4, 5, 6);   // Output: 123 456
    }
    

    【讨论】:

      猜你喜欢
      • 2012-04-09
      • 2019-03-23
      • 2014-04-12
      • 1970-01-01
      • 1970-01-01
      • 2016-12-01
      • 1970-01-01
      • 2011-07-25
      • 2015-05-21
      相关资源
      最近更新 更多