Plantillas variables: desplegar argumentos en grupos


16

Tengo una función que toma dos argumentos:

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

Y una variable que debe reenviar sus argumentos en pares:

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
}

Querría bar(1,2,3,4) llamar foo(1,2)yfoo(3,4)

Hay una manera de hacerlo ?


44
Es peligroso reenviar los mismos
argumentos

Respuestas:


13

Puedes lograrlo con sobrecargas.

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
}

EN VIVO


Tenga en cuenta que con el fragmento mínimo anterior al llamar barcon 0 o argumentos impares no obtendrá un error de función coincidente . Si desea un mensaje de compilación más claro static_assert, puede comenzar desde este fragmento .


5

Recurrencia simple 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)...);
}

Llámalo así:

bar(1, 2, 3, 4);

Manifestación

Yo diría que la respuesta de songyanyao es bastante canónica antes de C ++ 17. Posteriormente, if constexprnos ha permitido mover la lógica a los cuerpos de nuestras funciones en lugar de usar trucos de sobrecarga.


1
La versión de songyanyao es bastante fácil de extender, de modo que también toma la función que está aplicando como argumento. En mi opinión, esto es bastante bueno, ya que nos permite aplicar este patrón varias veces sin tener que escribir la lógica cada vez. ¿Hay una versión de su respuesta que permita lo mismo?
n314159

1
@ n314159: ¿Algo como esto ?
AndyG

1
¡Exactamente! Gracias. Personalmente, prefiero esto, ya que (además de lo que ya dije) separa la lógica de aplicación de la función a la que se aplica.
n314159

2

Generalización de C ++ 17 para nfunarios de canario:

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
}
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.