I'm really happy to have discovered for_each_arg(...)
, which makes dealing with argument packs much easier.
template<class F, class...Ts> F for_each_arg(F f, Ts&&...a) { return (void)initializer_list<int>{(ref(f)((Ts&&)a),0)...}, f; }
I'm, however, confused on its correct usage. There are many arguments that need to be perfectly forwarded, but am I performing any unnecessary forwarding?
Reading the code becomes harder with excessive fowarding.
struct UselessContainer
{
// Expects a perfectly-forwarded item to emplace
template<typename T> void add(T&&) { }
};
// Creates an `UselessContainer` already filled with `mArgs...`
auto makeUselessContainer(TArgs&&... mArgs)
{
using namespace std;
UselessContainer result;
for_each_arg
(
[&result, &mArgs...] // Am I capturing the `mArgs...` pack correctly here?
(auto&& mX) // Am I passing the arguments to the lambda correctly here?
{
// Is this `forward` necessary?
result.add(forward<decltype(mX)>(mX));
// Could it be replaced with
// `result.add(forward(mX));`
// ?
},
forward<TArgs>(mArgs)... // I assume this `forward` is necessary.
);
return result;
}
All my questions/doubts are expressed in the comments in the above code example.
Every
forward
in your code is indeed necessary to perfectly forward all arguments until the end. Names of rvalue references are lvalues, so unless you're forwarding everytime you pass arguments on, the value category information is lost.Also it is impossible to call
forward
without an explicit template argument list as the template parameter is only used in one, non-deduced context. In fact, a function template called without an explicit argument list cannot do the job.You can try a macro to somewhat shorten the code:
It then becomes
It's also possible to use a macro instead of
for_each_arg
in the first place:Just capture
&
when making this kind of lambda. If you must list, only&result
need be captured.forward<?>
is always used with a type parameter.Note Eric's for_each_arg is imperfect, and mostly about doing it in 140 characters or less. ;) Its imperfections are mild, and harmless here.
Here is an alternative:
First, write this:
it takes zero arg lambdas, and runs them left to right.
Then replace the call to
for_each_arg
with:the downside is that more compilers won't like the above.
Ordering of the expressions in the
do_in_order
is guaranteed by [dcl.init] and [dcl.init.list] sections in n4296 8.5.4/4 8.5.4/1 8.5/15 8.5/1. The initialization is a copy-list-initialization (8.5/15 and 8.5.4/1), is a "initializer-list of a braced-init-list" (8.5/1) and as such is sequenced left to right (8.5.4/4).