C++ Functor
Introduction
Functors, a short name for Function objects, is an instance of a certain class but can be called and executed like normal functions.
This can be achieved by overloading the function call operator (), and once overloaded, this allows us to call the objects of that specific class as if they were simply a function, meaning functors are objects in reality, but due to the overloading of the function call operator () we can make them behave like ordinary functions, and can directly call them using the normal function call syntax, by passing an argument and later can receive the returned value after the successful execution of the call as well.
Need of Functors Over Normal Functions
There are various reasons justifying why Functors should be preferred over normal Functions in their respective scenarios. For example
- Separation of Logic - While solving a problem using the functor-based approach, the appropriate logic can be separated from the other logical computations happening in the code, allowing us to use the functors in multiple situations as well.
- Parameterisation - Parameterising a functor is easier and more convenient than traditional functions.
- Statefulness - This is probably the most important part of why we should prefer functors over normal functions. Normal functions are like free functions, and they cannot retain states between function calls, but talking about functors, they can have the states, meaning the data we are working with will be remembered and will be carried between subsequent calls to a function or class methods there.
- Performance - Functors are most commonly used in STL implementations, and the concept of templates usually allows better optimization because more details are defined at compile time. As a result, passing functors or function objects in the program instead of ordinary functions often result in better performance.
Types of C++ Functors
There are generally three types of functors
- Generator Functors - Functors can be called without arguments.
- Unary Functors - Functors can be called with one argument.
- Binary Functors - Functors that can be called with two arguments.
How to Create a C++ Functor?
We can easily create functors in C++. For this purpose, we have to create a class first. Then we have to overload the function call operator (), and after that, we can create an instance or an object of that specific class and call that a function.
Let's understand this with the help of an example
Output:
Examples of C++ Functors
In the above example, we have seen how we can work with C++ functors, but one thing should be remembered that functors are not limited to printing string messages onto the console. We can do much more with C++ functors.
Let's see some more examples to understand it better
1. C++ Functor Example
In the example below, we will calculate the square of a number using C++ functors.
Output:
2. Example to Demonstrate State Retention of C++ Functors
In the below example, we will be having a demonstration of how functors in C++ preserve their states between subsequent function calls.
Output:
3. C++ Functor with Return Type and Parameter
Output:
4. C++ Functor with a Member and a Variable
Output:
5. C++ Predefined Functors with STL
The C++ language has a wide collection of predefined useful function objects or simply functors, defined inside the functional header file. We will be properly covering the C++ pre-defined functors in the ahead sections of the article.
Let's see an example of using a pre-defined Function in our program.
Output:
What are Pre-Defined Functors in C++?
The C++ language provides some predefined useful functors defined inside the functional header file. We can include the above header and use the functionalities of those functors immediately.
Let's now look at some predefined functors inside the functional header file.
Arithmetic Functors
Functors | Description |
---|---|
plus | takes two parameters and returns their sum |
minus | takes two parameters and returns their difference |
multiplies | takes two parameters and returns their product |
divides | takes two parameters and returns their division |
modulus | takes two parameters and returns the remainder after division |
negate | takes a single parameter and returns the negated value |
Relational Functors
Functors | Description |
---|---|
equal_to | takes two parameters and returns true if both are equal |
not_equal_to | takes two parameters and returns true if they are unequal |
greater | takes two parameters and returns true if the first parameter is greater than the second parameter |
greater_equal | takes two parameters and returns true if the first parameter is either greater than or equal to the second one |
less | takes two parameters and returns true if the first parameter is less than the second parameter |
less_equal | takes two parameters and returns true if the first parameter is either less than or is equal to the second one |
Logical Functors
Functors | Description |
---|---|
logical_and | returns the result of the Logical AND operation of two booleans |
logical_or | returns the result of the Logical OR operation of two booleans |
logical_not | returns the result of the Logical NOT operation of a boolean |
Bitwise Functors
Functors | Description |
---|---|
bit_and | performs the Bitwise AND operation on two parameters and returns the result |
bit_or | performs the Bitwise OR operation on two parameters and returns the result |
bit_xor | performs the Bitwise XOR operation on two parameters and returns the result |
Functors vs. Function Pointers vs. Virtual Functions
Each of the above three, i.e., functors, function pointers, and virtual functions, have their respective usages, advantages, and disadvantages. Firstly, let's see the difference between functions and function pointers.
Functors and Function Pointers
Functors can preserve their internal state, valid only for "this invocation" of the function object. In contrast, if we try to mimic the same behavior in normal functions or function pointers, we would need to initialize static variables in our program. Still, those static variables will still be valid for any specific function invocation.
Compilers can also inline the calls for the function objects, meaning the compiler can copy the code of the functor directly from its definition instead of creating a separate set of instructions in the memory, but this can not happen in the case of function pointers.
Functors and Virtual Functions
Functors and Virtual Functions are very closely related. Suppose there is a case in which we have to write a program to solve a maths question. The program is needed to take three parameters, two being both operands and the third parameter explaining what type of mathematical operation to perform. This scenario can easily be written using both functors and virtual functions, i.e., to choose what type of mathematical algorithm will be executed dynamically.
But the point where functor overpass the virtual functions here is that, when we use virtual functions, they do not give us the ability to write a templated function that can also accept function pointers, unlike functors. At the same time, this functionality may not be necessary while writing small programs, but it plays an important role while working with complex workflows, etc.
FAQs
Q: What are Functors in Simple Words?
A: Functors are simply objects of a certain class that can be made to behave like normal functions. We can call them using the function call syntax, pass parameters, receive the return value from them, etc.
Q: How are Functors made to behave like normal Functions?
A: It is due to the manual overloading of the function call operator (). The function call operator () can take any number of arguments of any datatype and return anything.
Related Articles:
Conclusion
- Functors or Function objects are an instance of a certain class but can be called and executed like normal functions.
- We can achieve this by overloading the function call operator ().
- Parameterization, statefulness, Improved performance, etc., are some reasons why functors can be preferred over normal functions.
- We can easily create a functor. For that, first, we need to create a class, then overload the function call operator (), create an instance of the class, and now we can call that object a normal function.
- C++ provides us with a wide range of useful pre-defined functors defined inside the functional header file, like arithmetic, relational, logical, bitwise, etc.
- Functors can generally be preferred over Function Pointers as they can preserve the internal state. Compilers can also inline the calls for function objects, resulting in improved program performance.
- Functors and Virtual Functions are very closely related. Both allow us to dynamically select between what type of arguments we want to pass in the function call.
- Using functors, we can write a templated function that can accept function pointers as well as the argument, unlike virtual functions.