Search code examples
c++templatesvariadic-templatesgeneric-programmingfunction-templates

How do I print out the arguments of a function using a variadic template?


This example uses a common variadic template and function. I want to print out the arguments passed to f:

#include <iostream>

template <typename T>
void print(T t) 
{
    std::cout << t << std::endl;
}

template <typename...T>
void f(T &&...args) 
{
    print(args...);
    f(args...);
}

int main() 
{
    f(2, 1, 4, 3, 5);
}

But I am getting the following errors:

Compilation finished with errors:<br>
source.cpp: In instantiation of '`void f(T ...)` [with `T = {int, int, int, int, int}`]':<br>
source.cpp:16:20: required from here <br>
source.cpp:10:4: error: no matching function for call to '`print(int&, int&, int&, int&, int&)`'<br>
source.cpp:10:4: note: candidate is:<br>
source.cpp:4:6: note: `template<class T> void print(T)`<br>
source.cpp:4:6: note: template argument deduction/substitution failed: 
source.cpp:10:4: note: candidate expects 1 argument, 5 provided

This is actually my first time using variadic functions and I do not exactly understand how to use them well.

I also do not get why this isn't working and what I can do to help it.


Solution

  • There you go. You had several mistakes in your code, you can see the comments between the lines below:

    #include <iostream>
    
    template <typename T>
    void print(T t) {
       std::cout << t << std::endl;
    }
    
    // Base case, no args
    void f() {}
    
    // Split the parameter pack.
    // We want the first argument, so we can print it.
    // And the rest so we can forward it to the next call to f
    template <typename T, typename...Ts>
    void f(T &&first, Ts&&... rest) {
        // print it
        print(std::forward<T>(first));
        // Forward the rest.
        f(std::forward<Ts>(rest)...);
    }
    
    int main() {
        f(2, 1, 4, 3, 5);
    }
    

    Note that using rvalue refs here makes no sense. You're not storing the parameters anywhere, so simply passing them by const reference should do it. That way you'd also avoid using std::forward just to keep the (useless) perfect forwarding.

    Therefore, you could rewrite f as follows:

    template <typename T, typename...Ts>
    void f(const T &first, const Ts&... rest) {
        print(first);
        f(rest...);
    }