Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Perfect forwarding and std::tuple (or other templated class)

I have some difficulties with perfect forwarding.

Here is my current level of understanding : glue Template + rvalue reference + std::forward and a special magical mode get activated where template deduction rules have not the same meaning as usual, but are crafted to allow perfect forwarding. Example :

template <typename T>
void outer(T&& t)
{
   inner(std::forward<T>(t)); // perfect forwarding activated
}

But what happen if T is actually a templated class ? For example, how can I perfect forward a std::tuple ? If use a T&& as aboce I will lost all type information of the objects contained in the tuple.
However the following code can't work :

template <typename... Args>
void outer(std::tuple<Args...>&& t) 
{
   inner(std::forward<std::tuple<Args...>>(t));
   use_args_types_for_something_else<Args...>(); // I need to have Args available
}

int main()
{
   std::tuple<int, double, float> t(4, 5.0, 4.0f);
   outer(t);
}

Last gcc snapshot says :

error: cannot bind 'std::tuple<int, double, float> lvalue to
std::tuple<int, double, float>&&

So clearly, we are still in the general, non-template case where lvalue can't bind to rvalue reference. "Perfect forwading mode" is not activated

So I tried to be sneaky and pass my tuple as a template template :

template <
  typename... Args
  template <typename...> class T
>
void outer(T<Args...>&& t) 
{
   inner(std::forward<T<Args...>>(t));
   use_args_type_for_something_else<Args...>(); 
}

But I still get the same error.

like image 383
Thomas Petit Avatar asked Nov 25 '11 17:11

Thomas Petit


1 Answers

Perfect forwarding works only if the type of the parameter is a template type for the function, so the only way to achieve perfect forwarding is like in your first example:

template <typename T>
void outer(T&& t)
{
   inner(std::forward<T>(t)); // perfect forwarding activated
}

The above works because it is a special case where T is deduced as SomeType& or SomeType&&.

This, however, does not mean that the type information for the tuple elements is lost for good. It is still retrievable (although I don't think you can typedef a variadic template pack). For example, you can still call use_args_types_for_something_else like this:

template <class T>
struct call_uses_args;

template <class ...Args>
struct call_uses_args<std::tuple<Args...>>
{
    void call() const { use_args_types_for_something_else<Args...>(); }
};

template <typename TupleT>
void outer(TupleT&& t)
{
   inner(std::forward<TupleT>(t));
   call_uses_args<typename std::remove_reference<TupleT>::type>().call();
}

There might be no good general solution, though, but hopefully such situations are rare. (E.g, in this particular example, it might be simpler just to overload outer.)

like image 139
UncleBens Avatar answered Oct 25 '22 15:10

UncleBens