3

I have come across two variants of std::forward usage with variadic template arguments.

template <typename... Args>
void foo(Args&&... arga)
{
    bar(std::forward<Args>(args)...);      // variant 1
    bar(std::forward<Args...>(args)...);   // variant 2

    // EDIT the above is a fixed version, the initial incorrect version was
    // bar(std::forward<Args>(args...));      // variant 1
    // bar(std::forward<Args...>(args...));   // variant 2

}

I have tried both variants with g++ and clang, and both appear to work equally well. No warnings produced with -Wall -Wextra -Wpedantic.

Are both variants correct? If not, why not? What does the standard have to say about it?

n. m. could be an AI
  • 112,515
  • 14
  • 128
  • 243
  • Even your updated question doesn't change much, variant 2 still does not work with two or more parameters. How have you actually tested your code? You need to [instantiate it with all the relevant combinations of parameters](http://coliru.stacked-crooked.com/a/a2695d46af54ce26) to see whether it actually works or not. – Daniel Frey Dec 19 '14 at 22:19
  • @DanielFrey apparently it wasn't adequately tested. – n. m. could be an AI Dec 19 '14 at 22:48

1 Answers1

16
bar(std::forward<Args>(args)...);      // variant 1

This is correct.

bar(std::forward<Args...>(args...));   // variant 2

This is wrong.

If the parameter pack is empty variant 1 expands to bar() which is valid, but variant 2 expands to bar(std::forward<>()) which is ill-formed because forward is missing its parameters.

If the pack has one element both variants expand to bar(std::forward<T>(t)) which is valid.

If the pack has two elements variant 1 correctly expands to bar(std::forward<T1>(t1), std::forward<T2>(t2)) which is valid. But for two elements variant 2 expands to bar(std::forward<T1, T2>(t1, t2)) which is ill-formed because std::forward has one template parameter and one function parameter.

So in all cases variant 1 works, but variant 2 is only valid for the case of a parameter pack with one element (and if you only care about a single template argument you shouldn't be using a variadic template at all!)

Basically a pack expansion should be PATTERN... where pattern is the thing you want to be repeated for each element in the pack. Since you want to call std::forward<T>(t) for each element, the PATTERN should be std::forward<Args>(args) and so the full pack expansion is that followed by ... (and not something with the ... inside the PATTERN, which results in two separate pack expansions, one of the types in Args and one of the variables in args).

Jonathan Wakely
  • 166,810
  • 27
  • 341
  • 521