问题描述
比较
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
在这方面是一种特殊情况(请参见)。
效果
编译器(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.
这篇关于为什么使用函数的函数?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!