问题
motivated by my previous question : Make variadic function which takes arbitary functors and returns a tuple of each return value of input functors
Now, I want to make a class which just execute each function object which may return void. To the best of my knowledge, I can make something like,
class A
{
public:
template <class Func>
void operator()(Func func)
{
func();
}
template <class First, class... Funcs>
void operator()(First first, Funcs... funcs)
{
first();
operator()(funcs...);
}
};
This code does the job.
However, I guess there must be much more clever way like the codes by Nawaz
and mfontanini
in the above link.
I tried below imitating their code.
class A
{
public:
template <class... Func>
void operator()(Func func...)
{
func()...;
}
};
However, gcc 4.7.2 doesn't compile. Am I missing here?
回答1:
You can do this:
template <class... Func>
void operator()(Func ... func) //corrected the syntax here
{
sink( (func(),0)... );
}
where sink
is defined as:
template<typename ...T> void sink(T...) {}
The purpose of sink
is to eat the value of the expression (func(),0)
which is just 0
irrespective of what func()
returns. It will work even if func()
return type is void
.
- Online Demo
Notice the in the demo above first two functions return something but the third one returns void
.
If you want the functions to be called from left-to-right, then you can use list-initialization syntax:
int sink[]{func(),0)... };
But it unnecessarily creates a variable. So I would define sink
as struct:
struct sink
{
template<typename ...T>
sink(T...) {} //templated constructor!
};
With this you can do this:
sink { (func(),0)... }; //note the curly-braces now!
Updated demo:
- Online Demo
Hope that helps.
回答2:
To expand on Nawaz' answer, you need a certain context to perform pack expansion - it can't just be done anywhere. One of those contexts is function arguments, another would be any kind if initialization.
The sink
function that Nawaz gave is a good starting point, but it has a serious deficiency - you don't have a guaranteed order in which the functors execute. If you want your users to be able to rely on the order they pass to you, you need a context that ensures left-to-right evaluation. Array initialization and list-initialization both fit here.
Using array initialization:
template <class... Func>
void operator()(Func func...)
{
int ignored[] = { (func(),0)... };
(void)ignored; // silence unused variable warnings
}
Using list-initialization:
struct swallow{
template<class... Ignored>
swallow(Ignored&&...)
};
template <class... Func>
void operator()(Func func...)
{
swallow{ (func(),0)... }; // create a temporary 'swallow' from all arguments
}
The (func(), 0)
part just executes func
, discards the return value, if any, and then evaluates to 0
.
Usually, if you want to do this for any functor, regardless of return type, and just throw away the results, you'd write it as (void(func()), 0)...
. The void(...)
part swallows whatever return value comes from func()
, if anything. This is needed because a user is allowed to overload operator,
, and might decide to do so for his own type and int
(the type of literal 0
), which would undermine your efforts. However, you can't overload operator,
with void
on any side.
来源:https://stackoverflow.com/questions/15062096/make-variadic-function-which-takes-arbitary-number-of-void-functors