Function Objects Function Objects Function Objects
Objects whose type provides at least one member function overload of
operator()
#include <iostream>
class Multiplier {
int m_;
public:
// constructor:
explicit constexpr Multiplier (int m) noexcept : m_{m} {}
// call operator
:
constexpr int operator () (int x) const noexcept { return m_ * x; }
};
int main () {
Multiplier triple{3};
int i = triple(2);
std::cout << "i: " << i << '\n';
}
Can be used like a function:…
Multiplier triple{3};
int i = triple(2); // i: 6
Can be stateful:…
#include <iostream>
class Accumulator {
int sum_ = 0;
public:
void operator () (int x) noexcept { sum_ += x; }
int total () const noexcept { return sum_; }
};
int main () {
Accumulator acc;
acc(2);
acc(3);
int sum = acc.total(); // sum: 5
std::cout << "sum: " << sum << '\n';
}
Can be used to customize behavior:…
// of, e.g., standard library algorithms:
if ( std::any_of(begin(v), end(v), in_interval{-2,8}) ) …
// custom function object ^
Example: Interval Query Example: in_interval Example
#include <iostream>
class in_interval {
int a_;
int b_;
public:
// constructor:
explicit constexpr
in_interval (int a, int b) noexcept: a_{a}, b_{b} {}
// call operator
:
[[nodiscard]] constexpr
bool operator () (int x) const noexcept {
return x >= a_ && x <= b_;
}
};
int main () {
// make an object
in_interval test {-10,5};
// invoke its operator()
std::cout << test(1) << '\n'; // true
std::cout << test(5) << '\n'; // true
std::cout << test(-12) << '\n'; // false
std::cout << test(8) << '\n'; // false
}
#include <iostream>
#include <vector>
#include <algorithm>
class in_interval {
int a_;
int b_;
public:
// constructor:
explicit constexpr
in_interval(int a, int b) noexcept: a_{a}, b_{b} {}
// call operator:
[[nodiscard]] constexpr
bool operator () (int x) const noexcept {
return x >= a_ && x <= b_;
}
};
int main () {
std::vector<int> v {9,0,4,1,8,3,7,2,9};
// find 1st value in interval [6,8]
// in a subrange of v (as shown in image):
auto i = find_if(begin(v)+2, begin(v)+7, in_interval{6,8});
if (i != end(v)) {
auto value = *i; // int value = 8
auto index = distance(begin(v),i); // int index = 4
…
std::cout << "value: " << value << '\n';
std::cout << "index: " << index << '\n';
std::cout << "-----------------------\n";
}
// 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 = 0
auto index = distance(begin(v),j); // int index = 1
…
std::cout << "value: " << value << '\n';
std::cout << "index: " << index << '\n';
}
}
Note that the relative order of elements within the resulting partitions need not be the same as in the original sequence.
#include <iostream>
#include <vector>
#include <algorithm>
class in_interval {
int a_;
int b_;
public:
// constructor:
explicit constexpr
in_interval(int a, int b) noexcept: a_{a}, b_{b} {}
// call operator:
[[nodiscard]] constexpr
bool operator () (int x) const noexcept {
return x >= a_ && x <= b_;
}
};
int main () {
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 2 0'
std::cout << '\n';
// print 2nd partition:
for_each(i, end(v), [](int x){ std::cout << x << ' '; }); // prints '6 4 8 3 5'
std::cout << '\n';
auto firstOf2ndPart = *i; // 6
auto splitIndex = distance(begin(v),i); // 3
std::cout << "firstOf2ndPart: " << firstOf2ndPart << '\n';
std::cout << "splitIndex: " << splitIndex << '\n';
}
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., for tracking status information) with a parallel standard algorithm, then make sure it is concurrency-safe. For example, access to resources that are shared between multiple threads, like e.g., I/O-streams has to be managed properly.
Standard Library Function Objects Standard Function Objects Std.Lib.
Comparisons
#include <functional>
std::equal_to
std::not_equal_to
std::greater
std::less
std::greater_equal
std::less_equal
#include <set>
#include <vector>
#include <functional>
#include <algorithm>
#include <iostream>
int main () {
// set with descending order (default is 'less'):
std::set<int,std::greater<>> s;
// compare with 'greater' instead of the default 'less':
std::vector<int> v1 = {1,4,5};
std::vector<int> v2 = {1,2,5};
std::cout << lexicographical_compare( begin(v1), end(v1), begin(v2), end(v2), std::greater<>{}) << '\n'; // true
}
#include <functional>
std::plus
std::minus
std::multiplies
std::divides
std::modulus
std::negate
Example: Left Fold Using Binary Operation
uses operator +
as default,
if no fold operation is given as 4th argument
⇒ result is sum of the input elements
#include <vector>
#include <functional>
#include <numeric>
#include <iostream>
int main () {
std::vector<int> v {1,2,3,4,5};
// using default (operator +):
int sum = accumulate(begin(v), end(v), 0); // sum 15
// using multiplication:
int product = accumulate(begin(v), end(v), 1, std::multiplies<>{}); // product = 120
std::cout << "sum: " << sum << '\n';
std::cout << "product: " << product << '\n';
}
Comments…