C++ Function Object

Introduction

Function object is any object for which the function call operator is defined, i.e., operator() is overloaded. It is also called functor.

Suppose we want to implement a function which requires a lot of parameters and the parameters would be set during runtime, using functors would be convenient because the parameters could be saved in the functor instance when it is created. This also means we can create a family of the functions using different parameters easily. In addition, by using C++ runtime polymorphism, i.e., function overriding, we could defer the functor type determination to runtime.

In this blog post, I would like to show some of the functor implementations.

Examples

The following example presents a couple of functors which use default and non-default parameters, how to pass functor to functions using pointer, and how to integrate functors with template.

functors.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
#include <iostream>
#include <vector>
#include <algorithm>

class CircleArea
{
public:
CircleArea()
{
}
CircleArea(double pi) : mPi{pi}
{
}
double operator() (double r) const
{
return this->mPi * r * r;
}
private:
double mPi{3.1415926};
};

template <class T>
class UnaryFunction
{
public:
virtual T operator() (T x) const = 0;
};

template <class T>
class Increase : public UnaryFunction<T>
{
public:
Increase(T val) : mVal{val}
{
}
T operator() (T x) const override
{
return x + this->mVal;
}
private:
T mVal;
};

template <class T>
class Decrease : public UnaryFunction<T>
{
public:
Decrease(T val) : mVal{val}
{
}
T operator() (T x) const override
{
return x - this->mVal;
}
private:
T mVal;
};

template <class T>
class BinaryFunction
{
public:
virtual T operator() (T x, T y) const = 0;
};

template <class T>
class Add : public BinaryFunction<T>
{
public:
T operator() (T x, T y) const override
{
return x + y;
}
};

template <class T>
class Minus : public BinaryFunction<T>
{
public:
T operator() (T x, T y) const override
{
return x - y;
}
};

template <class T>
T runBinaryFunction(T x, T y, BinaryFunction<T>* func)
{
return (*func)(x, y);
}

// Functors for template
template <class T, class BinaryFunc>
class Foo
{
public:
T runBinaryFunction(T x, T y)
{
return this->func(x, y);
}
private:
BinaryFunc func;
};

int main()
{
// Use default parameters for the functor
CircleArea computeCircleArea;
// Specify custom parameters for the functor
CircleArea computeCustomCircleArea{3.14};
double r{1.0};
std::cout << computeCircleArea(r) << std::endl;
std::cout << computeCustomCircleArea(r) << std::endl;

Add<double> addDouble;
Minus<double> minusDouble;
double x{1.1};
double y{2.2};
// Use functors
std::cout << addDouble(x, y) << std::endl;
std::cout << minusDouble(x, y) << std::endl;
// Use functor pointers
std::cout << runBinaryFunction(x, y, &addDouble) << std::endl;
std::cout << runBinaryFunction(x, y, &minusDouble) << std::endl;

std::vector<int> vec{1,2,3,4,5};
std::vector<int> vecIncreaseOne;
std::vector<int> vecIncreaseTen;
Increase<int> increaseOne{1};
Increase<int> increaseTen{10};
std::transform(vec.begin(), vec.end(), std::back_inserter(vecIncreaseOne), increaseOne);
std::transform(vec.begin(), vec.end(), std::back_inserter(vecIncreaseTen), increaseTen);
for (int val : vecIncreaseOne)
{
std::cout << val << " ";
}
std::cout << std::endl;
for (int val : vecIncreaseTen)
{
std::cout << val << " ";
}
std::cout << std::endl;

Foo<int, Add<double>> fooAdd;
std::cout << fooAdd.runBinaryFunction(1, 2) << std::endl;
Foo<int, Minus<double>> fooMinus;
std::cout << fooMinus.runBinaryFunction(1, 2) << std::endl;
}

To compile the program, please run the following command in the terminal.

1
$ g++ functors.cpp -o functors -std=c++11

Conclusions

Functors are less confusing and much easier to use compared to function pointers.

Author

Lei Mao

Posted on

02-10-2020

Updated on

02-10-2020

Licensed under


Comments