Functors in C++ are objects that can be called like a function as they provide many powerful features that many developers overlook sometimes. As functions play an important role in writing code efficiently, but sometimes we need a kind of function that retains state and works well with other functions or algorithms, then, functors are used. In this blog, we will discuss what a functor is, why it is useful, and how it can be implemented effectively in C++.
Table of Contents:
What is a Functor in C++?
A functor, or function object, is an object in C++ that can be called like a function using the operator(). Functors enable objects to act like functions while also maintaining state and encapsulating logic. It can be created by defining a class or structure that overloads the operator(). Also, functors are reusable and flexible.
Syntax of Functor:
class FunctorName {
public:
// Overload the function call operator
ReturnType operator()(ParameterList) {
// Function body
}
};
Example:
Output:
The code shows how the functor object f acts like a function when f() is called.
Need for Functors over Normal Functions in C++
1. Normal functions cannot maintain data over many calls, whereas functors can, and so functors are required.
2. Functors coexist smoothly with algorithms such as std::sort, std::for_each, and std::transform, whereas normal functions don't.
3. Functors are capable of accepting constructor arguments to specify dynamic behavior, whereas normal functions need additional parameters.
4. Functors retain state and context and are thus suited for event-based programming.
5. Functors may be inlined, which results in eliminating function call overhead over function pointers.
6. Functors enhance modularity and reusability by packaging data and function together.
Types of C++ Functors
There are three main types of functors in C++:
1. Generator Functors
A generator functor is a class overloaded for the operator(), which, when called, generates a sequence of values. It is useful when you require a stateful generator, e.g., generating unique identifiers, random numbers, or even Fibonacci numbers.
Example:
Output:
The code shows how the functor is used to generate and fill the vector with the generated numbers from a given value using the std::generate.
2. Unary Functors
A unary functor is an object of a function that accepts a single argument and performs an operation on it. Unary functors are commonly used in the Standard Template Library (STL), especially with algorithms such as std::transform and std::for_each. It is a class that overloads the operator() with one parameter.
Example:
Output:
The code shows how a functor is used to square numbers with the std::transform to compute the squared values.
3. Binary Functors
A binary functor is an object of a function that accepts two arguments and applies an operation to both of them. These functors are commonly used by STL algorithms, e.g., std::accumulate, std::sort, and std::transform (with two ranges of inputs). It is a class that overloads operator() with two parameters.
Example:
Output:
The code shows how a functor is used for addition with the std::accumulate to compute the sum of the input values in a vector.
Implementing Functors in C++
You can easily create and implement the functors in C++. Below are a few steps to implement the functors.
Steps to Implement a Functor:
Step 1: Create a class that will work as a function object.
Step 2: Implement the function logic inside the operator().
Step 3: Create an object of the class.
Step 4: Finally, use the object as a function.
Example to explain the implementation:
Output:
The code shows a functor is created, called, and used as a function in C++ programs.
Functors with Return Type and Parameter
A functor in C++ can accept parameters and return a value like a regular function. The return type depends on the logic that is implemented inside the operator().
Example:
Output:
The code shows how a functor takes an integer as input, which means it accepts parameters and returns six times its value using the overloaded operator().
Functor with a Member Variable in C++
A functor with a member variable can store and change internal state and also retains values across multiple calls. It is useful for operations like counters, accumulators, and scaling factors.
Example 1: Stateful Counter Functor
Output:
The code shows how a functor with a single member variable performs operations on inputs with a scaling factor.
Example 2: Functor with a Member Variable for Scaling
Output:
The code shows how the functors are used with STL algorithms in their operator implementations.
Example 3: Functor with Multiple Member Variables
Output:
The code shows how a functor with multiple member variables is used to uphold the running total of inputs with the scaling factor.
Functors with Standard Template Library(STL) in C++
Functors work comfortably with STL algorithms such as std::sort, std::for_each, and std::transform. They provide custom operations while keeping the code brief and efficient.
Example 1: Using Functor with std::sort
Output:
The code shows how functor mode sorts a vector in descending order with the std::sort.
Example 2: Using Functor with std::for_each
Output:
The code shows how the functor was used with std::for_each to print each number of the vector.
Retaining State in C++ Functor
Unlike regular functions, functors can retain state information by storing member variables. Thus, they allow carrying and changing data between calls.
How Functors Retain State
The functor has member variables to store values within the functor class and initialize the state upon creation of the functor, while the modified state is set in operator() on each call.
Example:
Output:
The code shows how the functor retains its state, which constitutes a counter function updating its count.
Predefined Functors in C++
C++ offers predefined functors about the functional header, which can be readily used with STL algorithms. These functors perform common arithmetic operations, comparisons, and logic operations. There are four primary categories of predefined functors.
1. Arithmetic Functors
The arithmetic functors are predefined function objects defined in the functional header. They work for basic arithmetic operations and can be used with STL algorithms like std::transform.
List of Arithmetic Functors
Functors | Operation |
std::plus<> | Addition |
std::minus<> | Subtraction |
std::multiplies<> | Multipliation |
std::divides<> | Division |
std::modulus<> | Modulus |
std::negate<> | Negation |
Example:
Output:
The code shows how the arithmetic functors are used to perform basic mathematical operations on the integers.
2. Relational Functors
Relational functors are predefined function objects in the functional header that are used to compare two values and produce a Boolean result, true or false. They are most often used in STL algorithms such as std::sort, std::count_if, and std::remove_if.
List of Relational Functors
Functors | Operation |
std::equal_to<> | Checks equality |
std::not_equal_to<> | Checks inequality |
std::greater<> | Checks if first parameter is greater |
std::less<> | Checks if the first parameter is smaller |
std::greater_equal<> | Checks greater or equal |
std::less_equal<> | Checks less than or equal |
Example:
Output:
The code shows how the relational functors are used to compare the integers and give 0 or 1 as an output.
3. Logical Functors
Logical functors are pre-defined function objects in the functional header that perform logical operations (AND, OR, NOT). They return a Boolean value, true or false, and are useful for filtering, conditional testing, and STL algorithms such as std::count_if and std::remove_if.
List of Logical Functors
Functors | Operation |
std::logical_and<> | Logical AND (&&) |
std::logical_or<> | Logical OR |
std::logical_not<> | Logical NOT (!) |
Example:
Output:
The code shows how the pre-defined logical functors perform logical operations on the boolean values and give true or false as an output.
4. Bitwise Functors
Bitwise functors are predefined function objects in the functional header that execute bitwise operations (AND, OR, XOR, NOT). Bitwise functors are useful for bit manipulation, bit-level calculations, and bitmasks.
List of Bitwise Functors
Functors | Operation |
std::bit_and<> | Bitwise AND (&) |
std::bit_or<> | Bitwise OR |
std::bit_xor<> | Bitwise XOR (^) |
std::bit_not<> | Bitwise NOT (~) |
Example:
Output:
The code shows how the pre-defined bitwise functors perform bitwise operations on integers.
Functors vs Function Pointers in C++
Both functors (function objects) and function pointers enable functions to be passed as arguments, but they differ fundamentally in flexibility and application.
Features | Functors | Function Pointers |
Definition | Objects with operator() | Pointer to a function |
Flexibility | Can store state (variables, members) | Stateless (cannot hold extra data) |
Performance | It can be inlined, reducing function call overhead | Function calls have extra overhead |
Customization | It can inherit, overload, and store extra data | Function calls have extra overhead |
Usage with STL | Works well with STL algorithms | Function calls have extra overhead |
Object-oriented | Supports encapsulation & inheritance | No encapsulation (raw function) |
Example:
Output:
The code shows the comparison between function pointers and functors for element-wise multiplication in a vector using std::transform.
Functors vs Virtual Functions in C++
Both virtual functions and functors (function objects) allow dynamic behavior in C++, but they have different uses and advantages.
Features | Functors | Virtual Function |
Definition | Object with operator() overloaded | Member function marked as virtual in a base class |
Dynamic Behavior | Achieved using stored state | Achieved via runtime polymorphism (vtable) |
Performance | Faster (can be inlined by compiler) | Slower (requires vtable lookup) |
Flexibility | Low (no vtable usage) | Higher (vtable pointer and function lookup) |
Memory Usage | Lower (only object overhead) | Higher (extra vtable pointer in objects) |
Usage in STL | Works well with STL algorithms | Not usable directly with STL |
Encapsulation | Not tied to class hierarchy | Part of a class hierarchy |
Function Overriding | Not possible | Supports overriding in derived classes |
Example:
Output:
The code shows how the functors are used for compile-time efficiency and virtual functions are used for runtime flexibility in modifying elements of the vector.
Functor with Lambda Compatibility in C++
A functor is a C++ design pattern that makes an object callable as if it were a function. Functors can be enhanced with lambda compatibility in modern C++ (C++11 and later).
Example:
Output:
The code shows how the functor is used with std::function, which provides flexibility by supporting both functors and lambda expressions.
Conclusion
Functors in C++ work as a function with better integration and a property of state retention, which provides us advantages in efficient programming. Understanding their advantages, types, and various uses provides us knowledge to develop more reusable and efficient code.
Functors in C++ - FAQs
1. What is a functor in C++?
A functor, or function object, is an object in C++ that can be called like a function using the operator().
2. Why use functors instead of normal functions?
You can use functors because they maintain state across calls, provide better integration with STL algorithms, support parameterized behavior, and can be inlined for better performance.
3. What are the types of functors in C++?
There are three main types of functors: Generator Functors, Unary Functors, and Binary Functors.
4. Why are the functors used with the Standard Template Library (STL)?
Functors are used with STL algorithms to customize their behavior efficiently.
5. How do functors improve performance?
Since functors can be inlined by the compiler, they avoid the overhead of function calls, making them more efficient than function pointers in many cases.