为什么在函数上使用函子?
比较
double average = CalculateAverage(values.begin(), values.end());
与
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)?
假设函子定义如下:
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:
关注点分离
在您的特定示例中,基于函子的方法具有将迭代逻辑与平均计算逻辑分离的优点.因此,您可以在其他情况下使用您的函子(想想 STL 中的所有其他算法),并且您可以将其他函子与 for_each
一起使用.
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
.
参数化
您可以更轻松地参数化函子.因此,例如,您可以有一个 CalculateAverageOfPowers
函子,它取数据的平方或立方体等的平均值,可以这样写:
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;
};
你当然可以用传统函数做同样的事情,但这样就很难与函数指针一起使用,因为它有一个与CalculateAverage
不同的原型.
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
.
状态
由于函子可以是有状态的,你可以这样做:
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.
请注意,几乎所有接受函子的 STL 算法/容器都要求它们是纯"谓词,即随着时间的推移没有可观察到的状态变化.for_each
是这方面的一个特例(参见例如 Effective Standard C++ Library - for_each vs. transforma>).
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).
性能
函数通常可以被编译器内联(毕竟 STL 是一堆模板).虽然理论上函数也是如此,但编译器通常不会通过函数指针内联.典型的例子是比较 std::sort
和 qsort
;假设比较谓词本身很简单,STL 版本通常要快 5-10 倍.
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 canonical example is to compare std::sort
vs qsort
; the STL version is often 5-10x faster, assuming the comparison predicate itself is simple.
总结
当然,可以使用传统的函数和指针来模拟前三个,但使用函子会变得更加简单.
Of course, it's possible to emulate the first three with traditional functions and pointers, but it becomes a great deal simpler with functors.
相关文章