Templat variadik: buka argumen dalam grup

16

Saya memiliki fungsi yang membutuhkan dua argumen:

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

Dan satu variadic yang harus meneruskan argumennya secara berpasangan:

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
}

Saya ingin bar(1,2,3,4)menelepon foo(1,2)danfoo(3,4)

Apakah ada cara untuk melakukan itu?

Fourmet
sumber
4
Berbahaya untuk meneruskan argumen yang sama dua kali
AndyG

Jawaban:

13

Anda dapat mencapainya dengan kelebihan.

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
}

HIDUP


Perhatikan bahwa dengan cuplikan minimum di atas saat menelepon bardengan argumen 0 atau ganjil Anda tidak akan mendapatkan kesalahan fungsi yang cocok . Jika Anda ingin pesan kompilasi yang lebih jelas dengan static_assertAnda dapat mulai dari potongan ini .

songyuanyao
sumber
5

Rekursi sederhana menggunakan 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)...);
}

Sebut seperti ini:

bar(1, 2, 3, 4);

Demo

Saya akan mengatakan bahwa jawaban songyanyao cukup kanonik pra-C ++ 17. Setelah itu, if constexprtelah memungkinkan kami untuk memindahkan logika ke badan fungsi kami daripada menggunakan trik kelebihan.

AndyG
sumber
1
Versi songyanyao cukup mudah untuk diperluas, sehingga dibutuhkan fungsi yang diterapkan sebagai argumen. Menurut pendapat saya ini cukup bagus, karena memungkinkan kita untuk menerapkan pola ini beberapa kali tanpa harus menulis logika setiap saat. Apakah ada versi jawaban Anda yang memungkinkan hal yang sama?
n314159
1
@ n314159: Sesuatu seperti ini ?
AndyG
1
Persis! Terima kasih. Secara pribadi saya lebih suka ini, karena (selain apa yang sudah saya katakan) itu memisahkan logika-berlaku dari fungsi yang diterapkan.
n314159
2

C ++ 17 generalisasi untuk n-ary functors:

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
}
Evg
sumber