how to capture a parameter pack by forward or move?
One of the few remaining useful things that can be done with std::bind
. The capturing is performed by bind
and the captured values are passed as arguments to a capture-less generic lambda:
template <typename... A>
auto test(A&&... a)
{
auto f = [](auto&&... a)
{
// use a...
};
return std::bind(f, std::forward<A>(a)...);
}
Live demo
The above works with Clang, but this GCC seems to have an issue with a spurious volatile
qualifier.
We can do it without bind
by capturing a tuple
in a second lambda that calls std::apply
(C++17) to unpack the tuple into the first lambda's parameter list:
template <typename... A>
auto test(A&&... a)
{
auto f = [](auto&&... a)
{
// use a...
};
return [f, tup = std::make_tuple(std::forward<A>(a)...)]() mutable { std::apply(f, tup); };
}
Live demo
Works with Clang and GCC; apply
is implemented with the indices trick that you wanted to avoid, but you are not exposed to it. The mutable
means the second lambda's call operator is non-const, so the tuple elements don't end up gaining a const
qualification.
C++20
C++20 has proper support for capturing by perfect forwarding:
template <typename... A>
auto test(A&&... a)
{
return [...a = std::forward<A>(a)]()
{
// use a...
};
}
One way would be to write a functor in the Haskell sense. Well a variardic one, which isn't very Haskell.
Write a function of signature (Ts...)->( ((Ts...)->X) -> X )
. Ie a function that takes a pack, and returns a function. The returned function can take a function taking that pack and evaluate it.
template<class...Ts>
auto make_functor(Ts&&...ts); // TODO
Once we have that we can solve your problem easily.
template<class ...A>
auto test(A&& ...a) {
return [unpack_a=make_functor(std::forward<A>(a)...)]() mutable
{
return unpack_a([&](auto&&...a){
// here you have access to a...
return sizeof...(a);
});
};
}
test
takes a pack, and returns a function that returns the size of that pack (well, does anything with the pack).
make_functor
is not easy: basically, we write a manual lambda, storing the args in a tuple, and unpacking the musing the indexes trick in an operator ().
In effect, we do the pack storing and unpacking once in a manual pseudo-lambda class, then get to reuse it later.
On second thought, it may be better to write a delayed apply that takes a tuple, stores it, then uses std::apply
later.
template<class...Ts>
auto delayed_apply(std::tuple<Ts...> tup){
return [tup=std::move(tup)](auto&&f)->decltype(auto) mutable{
return std::experimental::apply(decltype(f)(f), std::move(tup));
};
}
which lets the value/refness of parameters be not lost!
template<class ...A>
auto test(A&& ...a) {
return [unpack_a=delayed_apply(std::forward_as_tuple(std::forward<A>(a)...))]() mutable
{
return unpack_a([&](auto&&...a){
// here you have access to a...
return sizeof...(a);
});
};
}
this does require std::experimental::apply
.
If you want to store rvalues and leave lvalues as references:
unpack_a=delayed_apply(std::tuple<A...>(std::forward<A>(a)...))
If you want to store both l and r values:
unpack_a=delayed_apply(std::make_tuple(std::forward<A>(a)...))
as you can see, this approach gives lots of control.
If you need a std::experimental::apply
, there are reference implementations: better those than anything I write on a smartphone.
Note that make_functor
can be written in terms of delayed_apply
, but the opposite is ... not as true.
In case you are confused, unpack_a
takes a lambda and unpacks the tuple used to create unpack_a
into it. Basically we store one object that is the entire pack, then unpack it when we need it inside the body of the lambda.
A longer delayed_apply
that handles both const and non-const and maybe even rvalue overloads may be required if you want the unpacking to work "more than once" sometimss and "only once" other times. It will have to return a class, not a lambda. Annoying. Made the example code work, I think, still not compiling.
Fortunetally this kind of thing is write once, use many.