Function ObjectsFunction ObjectsFunction Objects
objects whose type provides at least one member function overload of
operator()
class Multiplier {
int m_;
public:
// constructor:
explicit constexpr Multiplier(int m) noexcept : m_{m} {}
// call operator
:
constexpr int operator () const noexcept (int x) { return m_ * x; }
};
can be used like a function:…
Multiplier triple{3};
int i = triple(2); // i: 6
can be stateful:…
class Accumulator {
int sum_ = 0;
public:
void operator () (int x) noexcept { sum_ += x; }
int total() const noexcept { return sum_; }
};
Accumulator acc;
acc(2);
acc(3);
int sum = acc.total(); // sum: 5
Example: Interval Query Example: in_interval Example
class in_interval {
int a_;
int b_;
public:
// constructor:
explicit constexpr
in_interval(int a, int b): a_{a}, b_{b} noexcept {}
// call operator
:
constexpr bool operator () (int x) const noexcept {
return x >= a && x <= b;
}
};
// make an object
in_interval test {-10,5};
// invoke its operator()
cout << test(1); // true
cout << test(5); // true
cout << test(-12); // false
cout << test(8); // false
std::vector<int> v {5,2,9,1,3,8,5,2,9,0};
// find 1st value in interval [6,8]
// in a subrange of v (as shown in image):
auto i = find_if(begin(v)+2, end(v)+7, in_interval{6,8});
if(i != end(v)) {
auto value = *i; // int value = 8
auto index = distance(begin(v),i); // int index = 3
…
}
// find 1st value in [-4,4] in all of v:
auto j = find_if(begin(v), end(v), in_interval{-4,4});
if(j != end(v)) {
auto value = *j; // int value = 2
auto index = distance(begin(v),j); // int index = 1
…
}
std::vector<int> v {1,4,6,0,2,8,3,5};
auto i = partition(begin(v), end(v), in_interval{-1,2});
// print 1st partition:
for_each(begin(v), i, [](int x){ std::cout << x << ' '; }); // prints '1 0 2'
// print 2nd partition:
for_each(i, end(v), [](int x){ std::cout << x << ' '; }); // prints '4 6 3 8 5'
auto value = *i; // int value = 4
auto index = distance(begin(v),i); // int index = 3
Guidelines
Stateful =
the current result of operator()
depends on previous calls of operator()
(e.g., because member variable values are both used for computing
the result and changed in the same call to operator()
)
Many (standard) algorithms do not guarantee any order
in which passed-in function objects are called
.
This is especially the case for the parallel versions of the
standard algorithms that were introduced with C++17.
This means passing a stateful function object might yield different results depending on the concrete implementation of a particular algorithm and on the state of the function object prior to passing it to the algorithm.
- Subsequent calls to
operator()
should be independent from each other. - Prefer to make
operator()
const, i.e., not alter the function object's state at all. - If you do use a non-const
operator()
, e.g., to track status information with a parallel standard algorithm, then make sure it is concurrency-safe (i.e., can be accessed from multiple threads simultaniously).