When I make an rvalue from a template parameter pack it does not compile, but if it is a 'simple' template parameter it compiles fine.
In this code for_each_in_tup1
compiles fine, but for_each_in_tup3
does not. I do not understand why this will not compile, but both GCC 9.2 and VC v142 agrees that it is wrong.
Why is this not valid syntax?
This example:
#include <tuple>
using namespace std;
template< typename Tuple, size_t N = 0>
void for_each_in_tup1( Tuple&& tup ) {
if constexpr(N < tuple_size<decay_t<Tuple>>::value)
for_each_in_tup1<Tuple, N + 1>(forward<Tuple>(tup));
}
template< template<typename...> typename Tuple, typename... Ts>
void for_each_in_tup2( const Tuple<Ts...>& tup) {
}
template< template<typename...> typename Tuple, typename... Ts>
void for_each_in_tup3( Tuple<Ts...>&& tup) {
}
void test_lazy() {
tuple<uint32_t, uint32_t> tup;
for_each_in_tup1(tup);
for_each_in_tup2(tup);
for_each_in_tup3(tup);
}
fails with:
<source>:20:22: error: cannot bind rvalue reference of type 'std::tuple<unsigned int, unsigned int>&&' to lvalue of type 'std::tuple<unsigned int, unsigned int>'
20 | for_each_in_tup3(tup);
| ^~~
<source>:15:39: note: initializing argument 1 of 'void for_each_in_tup3(Tuple<Ts ...>&&) [with Tuple = std::tuple; Ts = {unsigned int, unsigned int}]'
15 | void for_each_in_tup3( Tuple<Ts...>&& tup) {
| ~~~~~~~~~~~~~~~^~~
In for_each_in_tup1
you are using forwarding reference:
template< typename Tuple, size_t N = 0>
void for_each_in_tup1( Tuple&& tup )
when you pass lvalue, Tuple
is deduced to be Tuple&
, and after reference collpasing you will get for_each_in_tup1(Tuple&)
. lvalue can be bound to lvalue reference, that is why this code works. If you passed rvalue into for_each_in_tup1
, parameter would be Tuple&&
, and it could accept only rvalues.
In for_each_in_tup3
there is normal rvalue reference, which can bind only rvalues.
To call for_each_in_tup3
you have to cast tup
to rvalue for example by std::move
:
for_each_in_tup3(std::move(tup));