I do not understand why the following code is not valid.
#include <type_traits>
#include <tuple>
template<typename... Ts>
void funka( std::tuple<Ts...>&& v ) {
}
template<typename T>
void funkb( T&& v ) {
funka( std::forward<T>( v ) );
}
void funk() {
auto tup = std::tuple<int,int>( 1, 2 );
funkb( tup );
}
It fails with this error:
<source>: In instantiation of 'void funkb(T&&) [with T = std::tuple<int, int>&]':
<source>:24:16: required from here
<source>:10:10: error: cannot bind rvalue reference of type 'std::tuple<int, int>&&' to lvalue of type 'std::tuple<int, int>'
funka( std::forward<T>( v ) );
~~~~~^~~~~~~~~~~~~~~~~~~~~~~~
It compiles if I forward with a decay.
template<typename T>
void funkb( T&& v ) {
funka( std::forward<std::decay_t<T>>( v ) );
}
So the question is. Why is that not valid code? To me it seems that the resulting parameter type of funkb and funka is the same.
Thanks in advance
Why is that not valid code?
In the funkb( tup ) call, tup is an lvalue. Due to forwarding reference deduction rules, the type of the argument v in funka is deduced as std::tuple<int, int>&.
std::forward<std::tuple<int, int>&>( v ) doesn't move v - it still is an lvalue.
Lvalues do not bind to rvalue references.
It compiles if I forward with a decay.
std::decay_t removes all cv-qualifiers and references. It changes your forward call to something like: std::forward<std::tuple<int, int>>( v ).
When std::forward is invoked with a non-reference or an rvalue reference as its template parameter, it will move v.
If you use decay and pass in an rvalue to funkb, then decay does nothing as the type is already "decayed" (i.e. it's T).
If you pass in an lvalue however, then the type of the forwarding reference is T&, and when you decay that, you get T. This means that the result of std::forward will be an rvalue in both cases.
Now the code is not valid without decay because funka takes a rvalue reference, and you pass an lvalue to funkb which you then forward (preserving the value category). You're basically doing
int a;
int&& b = a;
As you can see, with decay, you will always get an rvalue, which can bind to a rvalue reference.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With