For each argument I need apply two nested function:
obj.apply(someFilter(arg)); // arg is one argument, but here
// should be an unpacking of args
I don't know how to write unpacking for such case.
I saw this:
pass{([&]{ std::cout << args << std::endl; }(), 1)...};
on wiki, but again don't know how to apply this for my case.
It's actually quite simple:
You can put arbitrary expression inside the unpack of an variadic templates argument pack:
obj.apply(someFilter(arg))...
This will give you the result of obj.apply as a coma seperated list. You can then pass it to a dummy function:
template<typename... Args> swallow (Args&&...) {}
swallow(obj.apply(someFilter(arg))...);
To swallow the comma seperated list.
Of course, this assumes that obj.apply returns some kind of object. If not you can use
swallow((obj.apply(someFilter(arg)), 0)...);
to make actual (non void) arguments
If you don't know what obj.apply` returns (result might have overloaded the comma operator), you can disable the use of custom comma operators by using
swallow((obj.apply(someFilter(arg)), void(), 0)...);
Should you actually need to evaluate the items in order (which doesn't seem very likely from the question), you can abuse array initialization syntax instead of using a function call:
using Alias=char[];
Alias{ (apply(someFilter(args)), void(), '\0')... };
Here is a robust way to do an arbitrary set of actions on a parameter pack. It follows the principle of least surprise, and does the operations in order:
template<typename Lambda, typename Lambdas>
void do_in_order( Lambda&& lambda, Lambdas&& lambdas )
{
std::forward<Lambda>(lambda)();
do_in_order( std::forward<Lambdas>(lambdas)... );
}
void do_in_order() {}
template<typename Args>
void test( Args&& args ) {
do_in_order( [&](){obj.apply(someFilter(std::forward<Args>(args)));}... );
}
Basically, you send a pile of lambdas at do_in_order, which evaluates them front to back.
I assume that the code has multiple args as a parameter pack? Try:
obj.apply( someFilter( arg )... );
as parameter unpacking applies to the expression, so each element of the parameter pack get's expanded into someFilter( arg ).
Related
I would like to write a function that applies a function to each element of a parameter pack. The functions returns a std::tuple with the results of each invocation.
However, if the applied function returns void, I have to do something else, so I have a different overload for this case. But, almost all the ways I've found to expand the parameter pack do not work with void expressions, so I had to resort to what you see below, which seems a weird trick.
template<typename F, typename ...Args>
requires requires { std::tuple{std::declval<F>(Args)...}; }
auto for_each(F f, Args ...args) {
return std::tuple{f(args)...};
}
template<typename F, typename ...Args>
auto for_each(F f, Args ...args)
[[maybe_unused]] int a[] = {(f(Members), 0)...};
}
Note that I have to declare a unused variable and mark it with the attribute.
Which is the best way to obtain the expected result here?
This trick is the way to go pre-C++17, except that you need an extra , 0 in the array to support zero-length packs.
In C++17 and newer, use a fold expression: (f(args), ...);.
Note that you forgot perfect forwarding. You should be doing F &&f, Args &&... args, and then (f(std::forward<Args>(args)), ...);, and similarly for the first function.
I also question the value of having such a function. I'd understand doing this to a tuple, but if you already have a pack, you can do this manually at the call site.
template< typename ... Args>
auto build_array(Args&&... args) -> std::array<typename std::common_
type<Args...>::type, sizeof...(args)>
{
using commonType = typename std::common_type<Args...>::type;
return {std::forward<commonType>(args)...};
}
int main()
{
auto data = build_array(1, 0u, 'a', 3.2f, false);
for(auto i: data)
std::cout << i << " ";
std::cout << std::endl;
}
Hey guys, I cannot understand the above code. So basically, the code is to write a function that takes any number of elements of any type, which can, in turn, be converted into a common type. The function should also return a container having all the elements converted into that common type, and it should also be fast to traverse. This is a books solution.
From what I understand <typename... Args> is to allow a variation of parameters. Then, (Args&&...args) also allows for a variety of parameters, but only rvalues? I do not understand the arrow notation and the rest of the function declaration. Like what is the difference between each of them. Additionally, the book also passes in ? for the templates such as, std::array<?,?>?
Finally, what does the return statement even mean (ending with an ellipsis?) ? and forward?
Sorry, I am rambling on, but I just cannot make sense and obtain a detailed overview of what is going on.
It would be really kind of you if you can elaborate on this?
but only rvalues?
When you see T&&,
if T is not a template parameter, then T&& means an rvalue reference to T, which can bind to rvalules only;
if T is a template parameter, then T&& means a forwarding/universal reference to T, which can bind to both rvalue and lvalues.
Therefore, in your case, since Args is a template parameter (precisely a type template parameter pack, number (2) here), Args&&... args expands to a comma separated sequence of function parameter declarations each of which has type a forwarding reference. For instance, if you pass 3 arguments to build_array, the deduction takes place as if you had a declaration like this:
template<typename Arg1, typname Arg2, typname Arg3>
auto build_array(Arg1&& arg1, Arg2&& arg2, Arg3&& arg3)
-> std::array<typename std::common_type<Arg1, Arg2, Arg3>::type, 3>
what does the return statement even mean (ending with an ellipsis?) ?
Again, ... is to expand some variadic thing in a comma separated sequence of things. So if args in
return {std::forward<commonType>(args)...};
is actually 3 things, then that statement is expanded to
return {std::forward<commonType>(arg1), std::forward<commonType>(arg2), std::forward<commonType>(arg3)};
Notice the position of the ellipsis. f(args)... is expanded to f(arg1), f(arg2), f(arg3), …, whereas f(args...) would be expanded to f(arg1, arg2, arg3, …).
and forward?
That's probably the less easy to understand bit, and would require a dedicated question. However many questions on that topic exist already, so you just have to search for them, rather than asking a new one. Here an answer of mine where I've most clearly explained the difference between std::move and std::forward. If you set understanding that answer of mine as your target, you'll understand std::forward (and std::move) and everything will be clearer.
I do not understand the arrow notation and the rest of the function declaration.
Essentially,
auto f(/* parameters */) -> SomeType
is equivalent to
SomeType f(/* parameters */)
with the advantage that in the former SomeType can refer to types that are in /* parameters */, if needed. In your case the return type makes use of Args.
the book also passes in ? for the templates such as, std::array<?,?>?
Probably the book is just trying to guide you through argument deduction, and it's using ? to mean "we don't know yet what it is; keep reading".
The function std::forward is declared like so:
template< class T >
T&& forward( typename std::remove_reference<T>::type& t ) noexcept;
Meaning it takes a single argument of type std::remove_reference<T>::type&.
However, std::forward can also be used with variadic arguments:
template <typename... Args>
void f(Args... args)
{
q(std::forward<Args>(args)...);
}
I understand variadic templates and unpacking them.
However, I don't understand the syntax of ... coming "after " the call to std::forward.
What does this syntax mean precisely? Why does it work? Can it be used with functions other than std::forward?
This is what is called pack expansion. It expands the parameter pack out with commas between each member of the pack so
q(std::forward<Args>(args)...);
says that for each member of the parameter pack args apply std::forward to it. The resulting code would look like
q(std::forward<Args>(args1), std::forward<Args>(args2), ..., std::forward<Args>(argsN));
This works with any expression, it doesn't just apply to std::forward. For instance you could add some value to each parameter like
q((args + 1)...);
and that would expand to
q((args1 + 1), (args2 + 1), ..., (argsN + 1));
template <class F, class... Args>
void for_each_argument(F f, Args&&... args) {
[](...){}((f(std::forward<Args>(args)), 0)...);
}
It was recently featured on isocpp.org without explanation.
The short answer is "it does it not very well".
It invokes f on each of the args..., and discards the return value. But it does so in a way that leads to unexpected behavior in a number of cases, needlessly.
The code has no ordering guarantees, and if the return value of f for a given Arg has an overloaded operator, it can have unfortunate side effects.
With some white space:
[](...){}(
(
f(std::forward<Args>(args)), 0
)...
);
We will start from the inside.
f(std::forward<Args>(args)) is an incomplete statement that can be expanded with a .... It will invoke f on one of args when expanded. Call this statement INVOKE_F.
(INVOKE_F, 0) takes the return value of f(args), applies operator, then 0. If the return value has no overrides, this discards the return value of f(args) and returns a 0. Call this INVOKE_F_0. If f returns a type with an overriden operator,(int), bad things happen here, and if that operator returns a non-POD-esque type, you can get "conditionally supported" behavior later on.
[](...){} creates a lambda that takes C-style variadics as its only argument. This isn't the same as C++11 parameter packs, or C++14 variadic lambdas. It is possibly illegal to pass non-POD-esque types to a ... function. Call this HELPER
HELPER(INVOKE_F_0...) is a parameter pack expansion. in the context of invoking HELPER's operator(), which is a legal context. The evaluation of arguments is unspecified, and due to the signature of HELPER INVOKE_F_0... probably should only contain plain old data (in C++03 parlance), or more specifically [expr.call]/p7 says: (via #T.C)
Passing a potentially-evaluated argument of class type (Clause 9) having a nontrivial copy constructor, a non-trivial move constructor, or a non-trivial destructor, with no corresponding parameter, is conditionally-supported with implementation-defined semantics.
So the problems of this code is that the order is unspecified and it relies on well behaved types or specific compiler implementation choices.
We can fix the operator, problem as follows:
template <class F, class... Args>
void for_each_argument(F f, Args&&... args) {
[](...){}((void(f(std::forward<Args>(args))), 0)...);
}
then we can guarantee order by expanding in an initializer:
template <class F, class... Args>
void for_each_argument(F f, Args&&... args) {
int unused[] = {(void(f(std::forward<Args>(args))), 0)...};
void(unused); // suppresses warnings
}
but the above fails when Args... is empty, so add another 0:
template <class F, class... Args>
void for_each_argument(F f, Args&&... args) {
int unused[] = {0, (void(f(std::forward<Args>(args))), 0)...};
void(unused); // suppresses warnings
}
and there is no good reason for the compiler to NOT eliminate unused[] from existance, while still evaluated f on args... in order.
My preferred variant is:
template <class...F>
void do_in_order(F&&... f) {
int unused[] = {0, (void(std::forward<F>(f)()), 0)...};
void(unused); // suppresses warnings
}
which takes nullary lambdas and runs them one at a time, left to right. (If the compiler can prove that order does not matter, it is free to run them out of order however).
We can then implement the above with:
template <class F, class... Args>
void for_each_argument(F f, Args&&... args) {
do_in_order( [&]{ f(std::forward<Args>(args)); }... );
}
which puts the "strange expansion" in an isolated function (do_in_order), and we can use it elsewhere. We can also write do_in_any_order that works similarly, but makes the any_order clear: however, barring extreme reasons, having code run in a predictable order in a parameter pack expansion reduces surprise and keeps headaches to a minimum.
A downside to the do_in_order technique is that not all compilers like it -- expanding a parameter pack containing statement that contains entire sub-statements is not something they expect to have to do.
Actually it calls function f for each argument in args in unspecified order.
[](...){}
create lambda function, that does nothing and receives arbitrary number of arguments (va args).
((f(std::forward<Args>(args)), 0)...)
argument of lambda.
(f(std::forward<Args>(args)), 0)
call f with forwarded argument, send 0 to lambda.
If you want specified order you can use following thing:
using swallow = int[];
(void)swallow{0, (f(std::forward<Args>(args)), 0)...};
I feel a bit uncomfortable at the moment when using parameter packs. I've got a function
template <class ... Args>
void f( int x, Args ... args, int y )
{}
and of course using it like this works:
f( 5, 3 );
I am wondering why the following call fails:
f( 5, 3.f, 3 );
It seems like a straight-forward use of parameter packs to me, but according to the compiler Args is not expanded.
Of course, I could easily replace f by:
template <class ... Args>
void f( int x, Args ... args )
{
static_assert( sizeof...( args ) >= 1, ... );
extract y from args ...
}
Questions:
Why can't I use parameter packs like this? It seems like the compiler could easily create the replacement code. Or are there any problems with the replacement f() above?
What is the best way to deal with this, if the order of the parameters is really important to me? (Think of std::transform for an arbitrary number of input iterators.)
Why is the usage of parameter packs not forbidden in the situation above? I assume, it is because they could be expanded explicitly, e.g. f<float>( 5, 3.f, 3 )?
Why can't I use parameter packs like this? It seems like the compiler could easily create the replacement code. Or are there any problems with the replacement f() above?
Might be easy in this particular case, might not be in others. The real answer is that the Standard specifies the behavior and the Standard did not specify any transformation... and in general rarely does.
I would note that your transformation does not work with SFINAE, since static_assert are hard-errors, so I am glad the transformation is not performed. (Hint: what if f is overloaded with a single-argument version taking a float, which would get chosen with/without transformation for f(1) ?)
What is the best way to deal with this, if the order of the parameters is really important to me? (Think of std::transform for an arbitrary number of input iterators.)
Explicitly specifying the template pack parameters.
Why is the usage of parameter packs not forbidden in the situation above? I assume, it is because they could be expanded explicitly, e.g. f( 5, 3.f, 3 )?
I think that your assumption is spot on; if explicitly specified this works as expected.
It's a case of you can have other non-variadic template parameters, but only after these non-variadic arguments have been allocated to the non-variadic parameters, can the remainders constitute the parameter pack for the variadic parameter. So you have to move the int y at the end of your argument list to before the variadic parameters. Something like:
template <class... Args>
void f(int x, int y, Args... args)
{}