Modelli variabili: spiega argomenti in gruppi


16

Ho una funzione che accetta due argomenti:

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

E una variadica che dovrebbe inoltrare i suoi argomenti in coppia:

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
}

Vorrei bar(1,2,3,4)chiamare foo(1,2)efoo(3,4)

C'è un modo per farlo?


4
È pericoloso inoltrare gli stessi argomenti due volte
AndyG

Risposte:


13

Puoi realizzarlo con sovraccarichi.

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
}

VIVERE


Tieni presente che con lo snippet minimo sopra riportato quando chiami barcon 0 o argomenti dispari non otterrai alcun errore di funzione corrispondente . Se desideri un messaggio di compilazione più chiaro con static_assertte, puoi iniziare da questo frammento .


5

Ricorsione semplice usando 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)...);
}

Chiamalo così:

bar(1, 2, 3, 4);

dimostrazione

Direi che la risposta di Songyanyao è piuttosto canonica prima del C ++ 17. Successivamente, ci if constexprha permesso di spostare la logica nei corpi delle nostre funzioni invece di usare trucchi di sovraccarico.


1
La versione di songyanyao è abbastanza facile da estendere, in modo tale da assumere anche la funzione che sta applicando come argomento. A mio avviso, questo è piuttosto carino, dal momento che ci consente di applicare questo modello più volte senza dover scrivere la logica ogni volta. Esiste una versione della tua risposta che consente lo stesso?
n314159

1
@ n314159: Qualcosa di simile a questo ?
AndyG,

1
Esattamente! Grazie. Personalmente preferisco questo, poiché (in aggiunta a quello che ho già detto) separa la logica di applicazione dalla funzione a cui è applicato.
n314159

2

Generalizzazione C ++ 17 per i nfunzioni -ary:

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
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.