C++ - C++ Function Objects and std::function?
Function objects are an important feature of C++ that allows an object to behave like a function. In simple terms, a function object is an object of a class or structure that provides a function-call operator, operator(). Because of this operator, the object can be called using parentheses just like an ordinary function. The C++ standard library uses function objects extensively, particularly with algorithms and other facilities in the <functional> header. (Cppreference)
1. What Is a Function Object?
Normally, when we want to perform an operation, we define a function:
#include <iostream>
using namespace std;
int square(int x)
{
return x * x;
}
int main()
{
cout << square(5);
return 0;
}
Here, square() is an ordinary function. We call it by writing:
square(5);
A function object achieves something similar through an object.
#include <iostream>
using namespace std;
class Square
{
public:
int operator()(int x)
{
return x * x;
}
};
int main()
{
Square obj;
cout << obj(5);
return 0;
}
The important part is:
int operator()(int x)
The function-call operator allows the object obj to be used as if it were a function:
obj(5);
Therefore, Square is a function-object type and obj is a function object.
2. Why Use Function Objects?
Function objects provide an advantage over ordinary functions because objects can contain data.
For example:
#include <iostream>
using namespace std;
class Multiply
{
private:
int factor;
public:
Multiply(int f)
{
factor = f;
}
int operator()(int value)
{
return value * factor;
}
};
int main()
{
Multiply doubleValue(2);
Multiply tripleValue(3);
cout << doubleValue(10) << endl;
cout << tripleValue(10) << endl;
return 0;
}
Output:
20
30
Here, the objects remember their own factor values.
Multiply doubleValue(2);
Multiply tripleValue(3);
The first object multiplies by 2, while the second multiplies by 3.
This is one of the major differences between a simple function and a function object: a function object can maintain state inside an object.
3. The operator() Function
The function-call operator is the key feature of a function object.
Its general form is:
return_type operator()(parameters)
{
// statements
}
For example:
class Calculator
{
public:
int operator()(int a, int b)
{
return a + b;
}
};
We can then write:
Calculator calculate;
cout << calculate(10, 20);
Instead of:
calculate.operator()(10, 20);
The first form is preferred because it looks and behaves syntactically like a normal function call.
4. Function Objects Can Store State
One of the most useful properties of function objects is their ability to store information between calls.
Consider:
class Counter
{
private:
int count = 0;
public:
void operator()()
{
count++;
cout << "Count: " << count << endl;
}
};
Now:
Counter counter;
counter();
counter();
counter();
produces:
Count: 1
Count: 2
Count: 3
The object retains the value of count.
This makes function objects useful when an operation needs to remember information.
5. Function Objects and STL Algorithms
Function objects are particularly useful with C++ Standard Library algorithms.
For example, std::sort() can receive a callable object that determines how elements should be compared.
#include <algorithm>
#include <iostream>
#include <vector>
using namespace std;
class Descending
{
public:
bool operator()(int a, int b)
{
return a > b;
}
};
int main()
{
vector<int> numbers = {5, 2, 8, 1, 9};
sort(numbers.begin(), numbers.end(), Descending());
for (int n : numbers)
{
cout << n << " ";
}
return 0;
}
Output:
9 8 5 2 1
Here, Descending() creates a temporary function object that tells sort() how two numbers should be compared.
This pattern is common throughout the C++ Standard Library. Function objects are used for operations such as comparisons, arithmetic, logical operations, searching, and sorting. (Cppreference)
6. What Is std::function?
A function object and std::function are related, but they are not the same thing.
std::function is a general-purpose function wrapper introduced in C++11. It can store different types of callable objects that match a specified function signature. These can include ordinary functions, function objects, lambda expressions, and other callable entities. (Cppreference)
It is defined in:
#include <functional>
The general syntax is:
std::function<return_type(parameter_types)> name;
For example:
std::function<int(int, int)> operation;
This means operation can hold something callable that:
-
accepts two
intvalues -
returns an
int
7. Storing an Ordinary Function in std::function
Consider this ordinary function:
int add(int a, int b)
{
return a + b;
}
We can store it inside std::function:
#include <functional>
#include <iostream>
using namespace std;
int add(int a, int b)
{
return a + b;
}
int main()
{
function<int(int, int)> operation = add;
cout << operation(10, 20);
return 0;
}
Output:
30
The important declaration is:
function<int(int, int)> operation = add;
It tells C++ that operation can hold a callable with this signature:
int(int, int)
8. Storing a Function Object in std::function
A custom function object can also be stored inside std::function.
#include <functional>
#include <iostream>
using namespace std;
class Multiply
{
public:
int operator()(int a, int b)
{
return a * b;
}
};
int main()
{
function<int(int, int)> operation = Multiply();
cout << operation(5, 4);
return 0;
}
Output:
20
The std::function wrapper hides the specific type of the callable object.
This is useful when different types of callable objects need to be handled through the same interface.
9. Storing Lambda Expressions
A lambda expression can also be stored in std::function.
#include <functional>
#include <iostream>
using namespace std;
int main()
{
function<int(int, int)> operation =
[](int a, int b)
{
return a + b;
};
cout << operation(10, 20);
return 0;
}
Output:
30
The lambda and ordinary function can therefore have the same std::function interface.
function<int(int, int)> operation;
The actual implementation can be changed without changing the code that calls operation.
10. Why std::function Is Useful
Suppose we want to perform different mathematical operations.
Without std::function, we might need separate functions:
int add(int a, int b);
int subtract(int a, int b);
int multiply(int a, int b);
With std::function, we can write a general function:
#include <functional>
#include <iostream>
using namespace std;
void calculate(int a, int b,
function<int(int, int)> operation)
{
cout << operation(a, b) << endl;
}
int add(int a, int b)
{
return a + b;
}
int main()
{
calculate(10, 5, add);
calculate(10, 5,
[](int a, int b)
{
return a * b;
});
return 0;
}
Output:
15
50
The calculate() function does not need to know whether the operation is an ordinary function, a lambda, or a function object. It simply receives something matching the required callable signature.
11. Passing Function Objects to Functions
Function objects can be passed to other functions just like other objects.
For example:
class Add
{
public:
int operator()(int a, int b)
{
return a + b;
}
};
void calculate(Add operation)
{
cout << operation(10, 20);
}
Then:
Add add;
calculate(add);
This is useful when the compiler knows the exact function-object type.
However, if a function needs to accept many different callable types, std::function can provide a common wrapper.
12. Function Object vs Ordinary Function
Consider the following ordinary function:
int square(int x)
{
return x * x;
}
And a function object:
class Square
{
public:
int operator()(int x)
{
return x * x;
}
};
Both can be called like:
square(5);
and:
Square{}(5);
However, the function object can contain data:
class Multiply
{
int factor;
public:
Multiply(int f) : factor(f) {}
int operator()(int x)
{
return x * factor;
}
};
Now different objects can represent different behavior:
Multiply byTwo(2);
Multiply byFive(5);
cout << byTwo(10);
cout << byFive(10);
The objects retain their respective state.
13. Function Object vs Lambda Expression
A lambda expression is also a callable object. The C++ language treats lambda expressions as function objects because they produce objects that can be invoked. (Cppreference)
For example:
auto add = [](int a, int b)
{
return a + b;
};
This can be viewed conceptually as an unnamed function-object type.
A traditional function object requires a class:
class Add
{
public:
int operator()(int a, int b)
{
return a + b;
}
};
A lambda is generally more concise:
auto add = [](int a, int b)
{
return a + b;
};
For simple operations, lambdas are often more convenient. For reusable objects with more complex state or behavior, a named function-object class can sometimes be clearer.
14. std::function Can Be Empty
A std::function object does not necessarily contain a callable.
For example:
std::function<void()> operation;
At this point, operation is empty.
Trying to invoke an empty std::function results in a std::bad_function_call exception. (Cppreference)
Therefore, it is useful to check whether it contains a callable:
if (operation)
{
operation();
}
Example:
#include <functional>
#include <iostream>
using namespace std;
int main()
{
function<void()> operation;
if (operation)
{
operation();
}
else
{
cout << "No function assigned";
}
return 0;
}
Output:
No function assigned
15. Callback Functions Using std::function
One of the most practical applications of std::function is implementing callbacks.
A callback is a function or callable object that is passed to another function and executed later.
For example:
#include <functional>
#include <iostream>
using namespace std;
void processData(function<void(int)> callback)
{
for (int i = 1; i <= 3; i++)
{
callback(i);
}
}
int main()
{
processData(
[](int value)
{
cout << "Processing: " << value << endl;
}
);
return 0;
}
Output:
Processing: 1
Processing: 2
Processing: 3
The processData() function does not decide what should happen to each value. The caller supplies the behavior through the callback.
This design is useful in event-driven programs, GUI applications, task systems, and other software where an action needs to be supplied dynamically.
16. std::function and Different Callable Types
One of the most important features of std::function is that different callable types can be placed behind the same interface.
For example:
#include <functional>
#include <iostream>
using namespace std;
int add(int a, int b)
{
return a + b;
}
class Multiply
{
public:
int operator()(int a, int b)
{
return a * b;
}
};
int main()
{
function<int(int, int)> operation;
operation = add;
cout << operation(4, 5) << endl;
operation = Multiply();
cout << operation(4, 5) << endl;
operation = [](int a, int b)
{
return a - b;
};
cout << operation(4, 5) << endl;
return 0;
}
Output:
9
20
-1
The same variable:
operation
can hold different callable targets as long as they satisfy the required signature.
This is the primary reason std::function is useful for flexible interfaces. (Cppreference)
17. std::function and std::bind
The <functional> library also provides std::bind, which can create a new callable by binding some arguments to an existing callable. (Cppreference)
For example:
#include <functional>
#include <iostream>
using namespace std;
int multiply(int a, int b)
{
return a * b;
}
int main()
{
auto doubleValue =
bind(multiply, 2, placeholders::_1);
cout << doubleValue(10);
return 0;
}
Output:
20
Here:
bind(multiply, 2, placeholders::_1)
creates a callable in which the first argument is fixed as 2.
Although std::bind is useful to understand, modern C++ code often uses lambda expressions for many situations where std::bind might previously have been used.
18. Standard Function Objects
C++ also provides predefined function objects.
For example:
std::plus<int>
represents addition, while:
std::minus<int>
represents subtraction.
Similarly, the standard library provides function objects for comparisons and logical operations. These predefined objects are part of the function-object facilities provided by <functional>. (Cppreference)
For example:
#include <functional>
#include <iostream>
using namespace std;
int main()
{
plus<int> add;
cout << add(10, 20);
return 0;
}
Output:
30
This demonstrates that even common operations can be represented as callable objects.
19. Advantages of Function Objects
Function objects provide several important benefits.
State retention
A function object can store data as member variables and use that data during calls.
Reusability
A function object can be defined once and used in multiple algorithms or parts of a program.
Compatibility with STL
Many Standard Library algorithms accept callable objects, making function objects useful for sorting, searching, transforming, and filtering operations.
Custom behavior
Programmers can create objects representing specific behavior rather than writing many separate functions.
Encapsulation
Data and the operation that acts on that data can be kept together inside a class.
20. Advantages of std::function
std::function adds another level of flexibility.
It can provide one common interface for different callable types, including ordinary functions, function objects, lambdas, and other supported callable targets. (Cppreference)
For example:
function<int(int)> operation;
can subsequently contain different implementations:
operation = squareFunction;
or:
operation = Square();
or:
operation = [](int x)
{
return x * x;
};
This makes it particularly useful for callback-based and configurable designs.
21. Disadvantages of std::function
std::function should not automatically be used everywhere.
A direct function object or template-based callable can often provide better compile-time optimization opportunities because the exact callable type is known to the compiler.
std::function is a type-erasing wrapper. It hides the concrete type of the callable, which provides flexibility but can introduce runtime and storage overhead. The implementation may use internal storage for small objects and dynamically allocated storage for larger objects; the exact performance characteristics therefore depend on the callable and implementation. (Cppreference)
For performance-critical code, it is therefore useful to understand whether the flexibility of std::function is actually required.
22. Function Object vs std::function
The distinction can be summarized as follows:
| Feature | Function Object | std::function |
|---|---|---|
| What is it? | An object that can be called like a function | A wrapper for callable objects |
| Main mechanism | operator() |
Type-erased callable wrapper |
| Can contain state? | Yes | Yes, depending on stored callable |
| Can store ordinary functions? | Not itself | Yes |
| Can store lambdas? | Lambda itself is a callable object | Yes |
| Can store different callable types in one variable? | Generally no | Yes |
| Header | Not necessarily required | <functional> |
| Introduced | Core C++ language concept | C++11 |
| Typical use | Custom behavior and STL algorithms | Flexible callbacks and callable interfaces |
23. Complete Example
The following program combines an ordinary function, a function object, a lambda, and std::function:
#include <functional>
#include <iostream>
using namespace std;
int add(int a, int b)
{
return a + b;
}
class Multiply
{
public:
int operator()(int a, int b)
{
return a * b;
}
};
int main()
{
function<int(int, int)> operation;
operation = add;
cout << "Addition: "
<< operation(10, 5) << endl;
operation = Multiply();
cout << "Multiplication: "
<< operation(10, 5) << endl;
operation = [](int a, int b)
{
return a - b;
};
cout << "Subtraction: "
<< operation(10, 5) << endl;
return 0;
}
Output:
Addition: 15
Multiplication: 50
Subtraction: 5
The key idea is that operation does not need to know the concrete type of the callable it contains. It only requires the callable to match:
int(int, int)
24. Important Points to Remember
A function object is an object that can be called like a function because its class defines operator().
The basic pattern is:
class MyFunction
{
public:
return_type operator()(parameters)
{
// operation
}
};
std::function is different. It is a general-purpose wrapper that can store different callable targets having a compatible signature. It is available through:
#include <functional>
Its syntax is:
std::function<return_type(parameter_types)> name;
For example:
std::function<int(int, int)> operation;
It can then hold an ordinary function:
operation = add;
a function object:
operation = Multiply();
or a lambda:
operation = [](int a, int b)
{
return a + b;
};
The central concept is therefore:
Function object = an object that behaves like a function.
std::function = a flexible wrapper that can store and invoke different kinds of compatible callable objects.
This distinction is important when working with the C++ Standard Library, callbacks, configurable operations, and modern C++ program design. (Cppreference)