Варіатичні шаблони: розгортайте аргументи в групах


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
Небезпечно двічі
пересилати одні й

Відповіді:


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
}

ЖИВИЙ


Зауважте, що при мінімальному фрагменті вище при дзвінку barз 0 або непарними аргументами ви не отримаєте помилки відповідної функції . Якщо ви хочете отримати більш чітке повідомлення про компіляцію, 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 є доволі канонічним до-С ++ 17. Згодом if constexprдозволив нам рухати логіку в тілах наших функцій замість того, щоб використовувати перенавантажувальні трюки.


1
Версію Songyanyao досить просто розширити, так що вона також приймає функцію, яку вона застосовує як аргумент. На мій погляд, це дуже приємно, оскільки дозволяє нам застосовувати цю схему кілька разів, не вимагаючи логіки кожного разу. Чи є версія Вашої відповіді, яка дозволяє те саме?
n314159

1
@ N314159: Що - щось на зразок цього ?
AndyG

1
Саме так! Дякую. Особисто я віддаю перевагу цьому, оскільки (крім того, що я вже говорив) він відокремлює логіку застосування від функції, до якої застосовується.
n314159

2

Узагальнення C ++ 17 для функціональних nфункторів:

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
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.