Alex Zywicki
Alex Zywicki

Reputation: 2313

Syntax differences in variadic template parameter pack forwarding

While working with variadic templates I have come across two different ways of writing a call to std::forward, but I am left wondering what the actual difference between the two syntaxes?

template<typename... T>
void one(T&&... args)
{
    foo(std::forward<T&&...>(args...));
}
template<typename... T>
void two(T&&... args)
{
    foo(std::forward<T&&>(args)...);
}

According to my compilers these are both valid syntax, and in most cases the compiler does not complain. But I have found some cases where one or the other is correct but the compiler does not say why in detail.

Is one more correct that the other?
Do they serve different purposes?

Upvotes: 3

Views: 194

Answers (1)

Ryan Haining
Ryan Haining

Reputation: 36802

The ... placement tells the compiler where to expand the pack, once for each element in the thing before it (hard to phrase easily but I'll illustrate below).

Let's consider a pack T = {int, double, char} and args = {1, 2.0, '3'}

Your first example (one) will expand T inside of the <> and then args inside of the () so it becomes

foo(std::forward<T&&...>(args...));  // becomes:
foo(std::forward<int&&, double&&, char&&>(1, 2.0, '3'));

This is not how std::forward works since it only expects one argument. Your second example (two) says to expand the whole call to std::forward once for each item in the packs, so it becomes

foo(std::forward<T&&>(args)...);  // becomes:
foo(std::forward<int&&>(1), std::forward<double&&>(2.0),std::forward<char&&>('3'));

As for why these both compiled fine, the result is identical if you called with only one argument. If you never called it at all then it wouldn't be instantiated.

Upvotes: 6

Related Questions