I have a function which ideally should look like this:
template<typename... t_buffers, t_function function>
void iterate_buffers(t_buffers &&... buffers, t_function &&function);
However, the parameter pack types can't be deduced unless it comes last, so instead my function signature is the following:
template<typename... t_buffers_and_function>
void iterate_bufferes(t_buffers_and_function &&... buffers_and_function);
The arguments then get shuffled around so that the function comes first. To perform this shuffling, I'm using the technique described here, but I'm hitting an issue when attempting to forward the tuple.
Here is a simplified example which has the problem:
template<typename... t_args>
void inner(std::tuple<t_args &&...> args) {};
template<typename... t_args>
void outer(t_args &&... args) {
inner(std::forward_as_tuple(std::forward<t_args>(args)...));
}
If I pass rvalues (e.g. by calling outer(1, 2);
), it works as expected. However if instead I pass lvalues (int x = 1; int y = 2; outer(x, y);
), I get the following syntax error:
error: could not convert ‘std::forward_as_tuple(_Elements&& ...) [with _Elements = {int&, int&}]((* & std::forward<int&>((* & args#1))))’ from ‘std::tuple<int&, int&>’ to ‘std::tuple<int&&, int&&>’
inner(std::forward_as_tuple(std::forward<t_args>(args)...));
~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
So forward_as_tuple
has produced a tuple of lvalue references std::tuple<int&, int&>
as expected, since the values provided to outer
are lvalues. Shouldn't inner
accept a tuple of either rvalues or lvalues, since std::tuple<t_args &&...> args
is using forwarding references? Why is it complaining about not being able to convert to rvalues? Are forwarding references disallowed in that context (since args is not simply T &&
) so args
is forced to be a tuple of rvalues only? (The examples I've found online seem to use std::tuple<t_args &&...>
syntax.)
The error goes away if I remove the usage of forwarding references from inner
: void inner(std::tuple<t_args...> args)
. Then the type of args
is std::tuple<int&, int&>
if I pass lvalues to outer
, and std::tuple<int&&, int&&>
if I pass rvalues. Maybe forwarding references aren't needed here because the correct lvalue/rvalue types have already been encoded into the tuple and don't need forwarding? I'm confused because all the examples I see online (including the one I linked) use the original call signature for inner
which has forwarding references.