In the previous article, we introduced the relevant applications of function pointers and stated that they are a low-level language construct inherited from C. In C++, objects are used as a more powerful method than C-style function pointers, and these objects are called function objects. Similar to function pointers, function objects behave like functions, but they are complete class-type objects that can have their own member variables and other member functions.
The key to creating a function object is to overload the function call operator. Below, we define a simple function object class and introduce its usage. The code is as follows:
#include <iostream>
int sumfunc(int *arr, int size)
{
int sum = 0;
for(int i = 0; i < size; i++)
{
sum += arr[i];
}
return sum;
}
class SumType
{
public:
int operator()(int *arr, int size) const
{
int sum = 0;
for(int i = 0; i < size; i++)
{
sum += arr[i];
}
return sum;
}
};
int main()
{
int arr[7] = {1, 3, 8, 9, 2, 11, 7};
// Function pointer
auto *fp = sumfunc;
std::cout << "Calling function pointer result: " << fp(arr, 7) << std::endl;
// Function object
SumType sum;
std::cout << "Calling function object result: " << sum(arr, 7) << std::endl;
}
This function object class is quite simple, with only one member (the function call operator). The function call operator is represented by operator(), and its actual parameter list is enclosed in parentheses following operator(). By creating an object using this class, it can be used just like an actual function. The code also compares the use of function pointers, and the compilation results are as follows:

Of course, like function pointers, function objects can also be used as parameters for other functions, enabling function callbacks. The code is as follows:
#include <iostream>
class SumType
{
public:
int operator()(int *arr, int size) const
{
int sum = 0;
for(int i = 0; i < size; i++)
{
sum += arr[i];
}
return sum;
}
};
int compare(int v, SumType &test)
{
int arr[7] = {1, 3, 8, 9, 2, 11, 7};
if(v < test(arr, 7))
return -1;
else if(v > test(arr, 7))
return 1;
else
return 0;
}
int main()
{
int v = 11;
// Function object
SumType sum;
std::cout << "Callback sum function, result: " << compare(v, sum) << std::endl;
}
The compilation results are as follows:

However, there is a problem now: function pointers and function objects cannot be used interchangeably as parameters because they have different types. If we want to support both, the compare function above would need to be defined twice as shown below.

This is too cumbersome, and this is where template type parameters come into play. We declare a template type parameter as the type for the compare function parameter, as shown below:

The complete code is as follows:
#include <iostream>
int sumfunc(int *arr, int size)
{
int sum = 0;
for(int i = 0; i < size; i++)
{
sum += arr[i];
}
return sum;
}
class SumType
{
public:
int operator()(int *arr, int size) const
{
int sum = 0;
for(int i = 0; i < size; i++)
{
sum += arr[i];
}
return sum;
}
};
template <typename T>
int compare(int v, T test)
{
int arr[7] = {1, 3, 8, 9, 2, 11, 7};
if(v < test(arr, 7))
return -1;
else if(v > test(arr, 7))
return 1;
else
return 0;
}
int main()
{
int v = 11;
// Function pointer
auto *fp = sumfunc;
std::cout << "Callback function pointer, result: " << compare(v, fp) << std::endl;
// Function object
SumType sum;
std::cout << "Callback function object, result: " << compare(v, sum) << std::endl;
}
Since T is a template type parameter, both function pointer and function object type parameters can be satisfied, and the code runs as follows:

Of course, the first parameter of compare can also be templated to make the functionality more powerful; this is just an example, so we won’t elaborate further.
So far, we have discussed the similar functionalities of function objects and function pointers, but we have not yet seen where function objects are stronger. Why did C++ introduce this? It requires defining a class and overloading the function call operator, which seems more complicated than function pointers. Remember what we said at the beginning of the article about function objects? They can have their own member variables and member functions, which is where their power lies. For example, with function pointers, if a callback needs to access user input values, it either has to extend the function’s parameters or use global variables. Below, we will illustrate this with examples. For instance, to implement a counter function that allows users to customize the starting count value and step size, and can return the count. Without using global variables, if we use function pointers, we can only extend the function’s parameters, and the code and results are as follows:

From the code, we can see that although the functionality is implemented, the state (count and step) is externalized, separating the state from the function. The caller needs to manually create and manage it, and every call requires passing all related state parameters, which is prone to errors. If we don’t want to pass all parameters every time, we can only use global variables. As shown below:

This makes the call simpler, but global variables break encapsulation, increase coupling, and reduce code maintainability. We previously mentioned that function objects are more powerful, so let’s take a look at how function objects are implemented. The specific code is as follows:
#include <iostream>
// Function object class, encapsulating state (step size and current value) and behavior (operator()) together
class Counter
{
private:
int m_count;
int m_step;
public:
// Constructor to initialize state
Counter(int count, int step) : m_count(count), m_step(step) {}
// Overload function call operator
int operator()()
{
m_count += m_step;
return m_count;
}
// Add methods to dynamically modify state
void set_count(int new_count)
{
m_count = new_count;
}
void set_step(int new_step)
{
m_step = new_step;
}
};
template <typename T>
int counter(T& test)
{
return test();
}
int main()
{
Counter ct(0, 1);
std::cout << counter(ct) << std::endl; // Output 1
std::cout << counter(ct) << std::endl; // Output 2
ct.set_step(2);
std::cout << counter(ct) << std::endl; // Output 4
return 0;
}
The compilation results are the same as before:

Needless to say, from the code call, it is clear that using function objects encapsulates state and methods within the same class, providing better encapsulation than using global variables. The calls are also simpler and type-safe, managing state modifications through class access control. In summary, the encapsulation, abstraction capabilities of function objects align better with modern C++ programming paradigms. By using object-oriented programming, data and the functions that operate on that data are packaged into a single, self-contained entity, making the code easier to understand and maintain, more flexible and extensible, and more efficient. Therefore, function objects are more powerful than function pointers. With that, we conclude our discussion on function objects, and we will talk about even more powerful lambda expressions in the future.