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; } };
A function assigns to every element of a set X an element of a set Y. A functor assigns to every object of a category C an object of a category D and also assigns to every morphism in C a morphism in D in a way compatible with sources, targets, and composition.
A function pointer allows a pointer to a function to be passed as a parameter to another function. Function Objects (Functors) - C++ allows the function call operator() to be overloaded, such that an object instantiated from a class can be "called" like a function.
A functor (or function object) is a C++ class that acts like a function. Functors are called using the same old function call syntax. To create a functor, we create a object that overloads the operator(). The line, MyFunctor(10); Is same as MyFunctor.
Overview. Function objects (also called functors) are an STL feature that you may not employ immediately when you start using the STL.
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 canonical 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.
Advantages of Functors:
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With