What is the easiest way to print a variadic parameter pack using std::ostream?

Without recursive calls and commas where you wanted.

In c++11 / c++14 through parameter pack expansion:

template <typename Arg, typename... Args>
void doPrint(std::ostream& out, Arg&& arg, Args&&... args)
{
    out << std::forward<Arg>(arg);
    using expander = int[];
    (void)expander{0, (void(out << ',' << std::forward<Args>(args)), 0)...};
}

DEMO


In c++17 using fold expressions:

template <typename Arg, typename... Args>
void doPrint(std::ostream& out, Arg&& arg, Args&&... args)
{
    out << std::forward<Arg>(arg);
    ((out << ',' << std::forward<Args>(args)), ...);
}

DEMO 2


In C++17, there will be an easier way (as hinted at by Kerrek SB in comments; this was actually present in N4606, the first post-C++14 draft), called fold expressions:

The code would be:

(out << ... << args);

and the pattern expression op ... op parameter-pack is called a binary left fold, whose definition is equivalent to ((( expression op arg1) op arg2) op arg3) .... op argN.

I think the outer parentheses are not strictly necessary for an expression-statement like this, but if the fold expression is an operand of another operator then they are either required, or a very good idea :)


The usual answer is to define two separate overloads, with an empty one for the base case:

// base case
void doPrint(std::ostream& out) {}

template <typename T, typename... Args>
void doPrint(std::ostream& out, T t, Args... args)
{
    out << t;                // add comma here, see below
    doPrint(out, args...);
}

Of course in real code I wouldn't make copies of the arguments each time and instead use forwarding references, but you get the idea.

If you want to add commas after every item, even after the last one, just replace out << t with out << t << ','.

If you only want commas on the inside, not past the last element, you need a separate one-argument overload which doesn't print the comma, and a generic overload take two distinct arguments before the pack, i.e:

template <typename T>
void doPrint(std::ostream& out, T t)
{
    out << t;
}

template <typename T, typename U, typename... Args>
void doPrint(std::ostream& out, T t, U u, Args... args)
{
    out << t << ',';
    doPrint(out, u, args...);
}