C++11 std::forward_as_tuple and std::forward

tower120 picture tower120 · Aug 21, 2014 · Viewed 7.5k times · Source

Should I std::forward my function parameters when I use them as arguments to std::forward_as_tuple?

template<class ... List>
void fn(List&& ... list){
   // do I need this forward?
   call_fn( forward_as_tuple( forward<List>(list)... ) );  
}

I know that they will be stored as rvalue references, but is there anything else I should consider?

Answer

Praetorian picture Praetorian · Aug 21, 2014

You must use std::forward in order to preserve the value category of the argument(s) to fn(). Since the arguments have a name within fn, they are lvalues, and without std::forward they will always be passed as such to std::forward_as_tuple.

The difference can be demonstrated using the following example:

template<typename T>
void bar2(T&& t)
{
    std::cout << __PRETTY_FUNCTION__ << ' '
               << std::is_rvalue_reference<decltype(t)>::value << '\n';
}

template<typename T>
void bar1(T&& t)
{
    std::cout << __PRETTY_FUNCTION__ << ' '
              << std::is_rvalue_reference<decltype(t)>::value << '\n';
    bar2(std::forward<T>(t));
    bar2(t);
}

bar1 always passes it arguments on to bar2, once with std::forward and once without. Now let's call them with an lvalue and an rvalue argument.

foo f;
bar1(f);
std::cout << "--------\n";
bar1(foo{});

Output:

void bar1(T&&) [with T = foo&] 0
void bar2(T&&) [with T = foo&] 0
void bar2(T&&) [with T = foo&] 0
--------
void bar1(T&&) [with T = foo] 1
void bar2(T&&) [with T = foo] 1
void bar2(T&&) [with T = foo&] 0

As you can see from the output, in both cases, without the use of std::forward, the argument is being passed as an lvalue to bar2.