可变参数模板:组中展开参数


16

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

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)

有没有办法做到这一点 ?


4
两次转发相同的参数很危险
AndyG

Answers:


13

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

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
}

生活


请注意,使用bar0或奇数参数进行调用时,使用上面的最小代码段,您将不会获得匹配的函数错误。如果您希望获得更清晰的编译消息,则static_assert可以从以下代码段开始。


5

简单的递归使用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);

演示版

我会说Songyanyao的答案是C ++ 17之前的规范。之后,if constexpr允许我们将逻辑移入函数的主体,而不是使用重载技巧。


1
songyanyao的版本很容易扩展,因此它也将要应用的功能作为参数。我认为这很好,因为它允许我们多次应用此模式,而不必每次都编写逻辑。您的答案是否有允许相同版本的版本?
n314159

1
@ n314159:像这样吗?
AndyG '19

1
究竟!谢谢。我个人比较喜欢这样做,因为(除了我已经说过的话)它将apply-logic与它所应用的功能分开了。
n314159

2

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
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.