为什么使用函数的函数? [英] Why use functors over functions?

查看:154
本文介绍了为什么使用函数的函数?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

比较

  double average = CalculateAverage(values.begin(),values.end()); 

  double average = std :: for_each(values.begin(),values.end(),CalculateAverage()); 

在函数上使用函子有什么好处?



假设函子的定义如下:

  class CalculateAverage 
{
private:
std :: size_t num;
double sum;
public:

CalculateAverage():num(0),sum(0)
{
}

void operator double elem)
{
num ++;
sum + = elem;
}

操作符double()const
{
return sum / num;
}
};


解决方案

至少有四个好的原因:



分离问题



在您的特定示例中,基于函子的方法具有分离来自平均值计算逻辑的迭代逻辑。所以你可以在其他情况下使用你的函子(考虑STL中的所有其他算法),你可以使用 for_each 的其他函数。



参数化



您可以更容易地参数化函子。例如,你可以有一个 CalculateAverageOfPowers 函数,它接受你的数据的平方,或立方体等的平均值,这样写:

  class CalculateAverageOfPowers 
{
public:
CalculateAverageOfPowers(float p):acc(0),n 0),p(p){}
void operator()(float x){acc + = pow(x,p); n ++; }
float getAverage()const {return acc / n; }
private:
float acc;
int n;
float p;
};

你当然可以用传统函数做同样的事情,函数指针,因为它与 CalculateAverage 有不同的原型。



状态性 p>

作为函子可以是有状态的,你可以这样做:

  CalculateAverage avg; 
avg = std :: for_each(dataA.begin(),dataA.end(),avg);
avg = std :: for_each(dataB.begin(),dataB.end(),avg);
avg = std :: for_each(dataC.begin(),dataC.end(),avg);

来平衡多个不同的数据集。



请注意,几乎所有接受函子的STL算法/容器都要求它们是纯谓词,即随时间没有可观察到的状态变化。 for_each 在这方面是一种特殊情况(请参见有效的标准C ++库 - for_each vs. transform )。



效果



编译器(STL是一堆模板,毕竟)。虽然理论上同样的函数,编译器通常不会通过一个函数指针。典型的例子是比较 std :: sort vs qsort ; STL版本通常快5-10倍,假设比较谓词本身很简单。



摘要



当然,可以用传统的函数和指针来模拟前三个,但是使用functor变得更简单。


Compare

double average = CalculateAverage(values.begin(), values.end());

with

double average = std::for_each(values.begin(), values.end(), CalculateAverage());

What are the benefits of using a functor over a function? Isn't the first a lot easier to read (even before the implementation is added)?

Assume the functor is defined like this:

class CalculateAverage
{
private:
   std::size_t num;
   double sum;
public:

   CalculateAverage() : num (0) , sum (0)
   {
   }

   void operator () (double elem) 
   {
      num++; 
      sum += elem;
   }

   operator double() const
   {
       return sum / num;
   }
};

解决方案

At least four good reasons:

Separation of concerns

In your particular example, the functor-based approach has the advantage of separating the iteration logic from the average-calculation logic. So you can use your functor in other situations (think about all the other algorithms in the STL), and you can use other functors with for_each.

Parameterisation

You can parameterise a functor more easily. So for instance, you could have a CalculateAverageOfPowers functor that takes the average of the squares, or cubes, etc. of your data, which would be written thus:

class CalculateAverageOfPowers
{
public:
    CalculateAverageOfPowers(float p) : acc(0), n(0), p(p) {}
    void operator() (float x) { acc += pow(x, p); n++; }
    float getAverage() const { return acc / n; }
private:
    float acc;
    int   n;
    float p;
};

You could of course do the same thing with a traditional function, but then makes it difficult to use with function pointers, because it has a different prototype to CalculateAverage.

Statefulness

And as functors can be stateful, you could do something like this:

CalculateAverage avg;
avg = std::for_each(dataA.begin(), dataA.end(), avg);
avg = std::for_each(dataB.begin(), dataB.end(), avg);
avg = std::for_each(dataC.begin(), dataC.end(), avg);

to average across a number of different data-sets.

Note that almost all STL algorithms/containers that accept functors require them to be "pure" predicates, i.e. have no observable change in state over time. for_each is a special case in this regard (see e.g. Effective Standard C++ Library - for_each vs. transform).

Performance

Functors can often be inlined by the compiler (the STL is a bunch of templates, after all). Whilst the same is theoretically true of functions, compilers typically won't inline through a function pointer. The canoncial example is to compare std::sort vs qsort; the STL version is often 5-10x faster, assuming the comparison predicate itself is simple.

Summary

Of course, it's possible to emulate the first three with traditional functions and pointers, but it becomes a great deal simpler with functors.

这篇关于为什么使用函数的函数?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆