Function Objects
Often we want to write numerical algorithms to be general, so we’d like to be able to
pass a function as an argument for the algorithm to operate on. std::function
provides
the mechanism for us to pass a function to a function. It has the form:
std::function<return_type(arg1_type, arg2_type)> f
where we specify both the function’s return type (as return_type
)
and the type of any arguments that the function takes (as
arg1_type
and arg2_type
above, for a function with 2
arguments).
First example
Here we write a function doit()
that takes a value and a function
that operates on that value and it just prints out some information
before and after calling the passed-in function.
#include <iostream>
#include <functional>
void doit(double x, std::function<double(double)> f) {
std::cout << "calling our function" << std::endl;
double r = f(x);
std::cout << "result = " << r << std::endl;
}
double f(double x) {
return x*x*x;
}
int main() {
double x{2};
doit(x, f);
}
Templated example
Here’s a more complicated example that performs an operator on an
input vector, where the operation is determined by a function passed
into our reduce()
function.
To make it more general, we template everything, so our reduce()
can operate on a vector of any type. (Note: there are routines in the
C++ SL that already do this, but here we can see a simple
implementation.)
We will apply a function of the form:
template <typename T>
T op(T x, T y);
to the elements of the vector. We assume that the order of x
and y
doesn’t matter.
#include <iostream>
#include <functional>
#include <vector>
#include <limits>
#include <cmath>
template <typename T>
T add(T x, T y) {
return x + y;
}
template <typename T>
T max(T x, T y) {
return std::max(x, y);
}
template <typename T>
T reduce(std::vector<T>& v, T init, std::function<T(T, T)> f) {
T val{init};
for (auto e : v) {
val = f(val, e);
}
return val;
}
int main() {
std::vector<int> a{0, 1, 2, 3, 4, 5};
std::cout << "sum a: " <<
reduce<int>(a, 0, add<int>) << std::endl;
std::cout << "max a: " <<
reduce<int>(a, std::numeric_limits<int>::min(), max<int>) << std::endl;
std::vector<double> b{-1.32, M_PI, 20.0, 1.e3, std::sqrt(2.0)};
std::cout << "sum b: " <<
reduce<double>(b, 0.0, add<double>) << std::endl;
std::cout << "max b: " <<
reduce<double>(b, std::numeric_limits<double>::lowest(), max<double>) << std::endl;
}
Note
In some cases, we can remove the specialization in the reduce()
call
if the compiler can infer it from the arguments. For instance, we can
do:
std::cout << "sum b: " <<
reduce(b, 0.0, add<double>) << std::endl;
But note that this does not work if we write 0
instead of 0.0
.
Tip
We can also use Lambda Functions with the same interface, e.g.,
std::cout << "min b: " <<
reduce<double>(b, std::numeric_limits<double>::max(),
[] (double x, double y) {return std::min(x, y);}) << std::endl;