Make variadic function which takes arbitary number of void functors

Deadly 提交于 2020-01-15 04:26:08

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!