Ready to practice?
Sign up to access interactive coding exercises and track your progress.
Function Call Operator Overloading
Create function objects (functors) callable with the () operator.
Overloading the parenthesis operator
The parenthesis operator (operator()) is unique because it allows you to vary both the type and number of parameters. It must be implemented as a member function.
Multiple parameters with operator()
Let's create a Grid class for a simple game board where we can access elements using (row, column) notation:
#include <iostream>
#include <cassert>
class Grid
{
private:
static constexpr int m_size{3};
int m_data[m_size][m_size]{};
public:
int& operator()(int row, int col)
{
assert(row >= 0 && row < m_size);
assert(col >= 0 && col < m_size);
return m_data[row][col];
}
const int& operator()(int row, int col) const
{
assert(row >= 0 && row < m_size);
assert(col >= 0 && col < m_size);
return m_data[row][col];
}
};
int main()
{
Grid board{};
board(0, 0) = 1;
board(1, 1) = 5;
board(2, 2) = 9;
std::cout << "Center: " << board(1, 1) << '\n';
std::cout << "Corner: " << board(0, 0) << '\n';
return 0;
}
Output:
Center: 5
Corner: 1
This is much clearer than using nested subscript operators (board[row][col])!
Zero parameters with operator()
The parenthesis operator can also take zero parameters. Let's add a reset function:
#include <iostream>
#include <cassert>
class Grid
{
private:
static constexpr int m_size{3};
int m_data[m_size][m_size]{};
public:
int& operator()(int row, int col)
{
assert(row >= 0 && row < m_size);
assert(col >= 0 && col < m_size);
return m_data[row][col];
}
void operator()()
{
for (int i{0}; i < m_size; ++i)
for (int j{0}; j < m_size; ++j)
m_data[i][j] = 0;
}
};
int main()
{
Grid board{};
board(0, 0) = 5;
board(1, 1) = 10;
std::cout << "Before reset: " << board(0, 0) << '\n';
board(); // Reset the grid
std::cout << "After reset: " << board(0, 0) << '\n';
return 0;
}
However, using board() to reset isn't very intuitive. A better approach would be a named member function like reset() or clear(). The parenthesis operator is most useful when the operation truly feels like a function call with parameters.
Functors (function objects)
One powerful use of operator() is creating functors - objects that act like functions but can maintain state:
#include <iostream>
class Multiplier
{
private:
int m_factor{1};
public:
explicit Multiplier(int factor) : m_factor{factor} {}
int operator()(int value) const
{
return value * m_factor;
}
};
int main()
{
Multiplier timesTwo{2};
Multiplier timesTen{10};
std::cout << timesTwo(5) << '\n'; // 10
std::cout << timesTwo(7) << '\n'; // 14
std::cout << timesTen(5) << '\n'; // 50
std::cout << timesTen(7) << '\n'; // 70
return 0;
}
Each Multiplier object behaves like a function, but maintains its own multiplication factor. This is useful when you need function-like behavior with associated data.
A more complex example
Here's a Calculator functor that accumulates results:
#include <iostream>
class Calculator
{
private:
int m_total{0};
public:
int operator()(int value)
{
m_total += value;
return m_total;
}
void reset() { m_total = 0; }
int getTotal() const { return m_total; }
};
int main()
{
Calculator calc{};
std::cout << calc(10) << '\n'; // 10
std::cout << calc(5) << '\n'; // 15
std::cout << calc(-3) << '\n'; // 12
Calculator calc2{};
std::cout << calc2(100) << '\n'; // 100 (independent from calc)
return 0;
}
When to use operator()
Use operator() when:
- You need multiple parameters to index or access data (like 2D coordinates)
- You're creating a functor that maintains state while acting like a function
- The operation naturally reads like a function call
Avoid operator() when:
- A named member function would be clearer
- The operation doesn't feel like a function call
Summary
Parenthesis operator (()): Must be implemented as a member function. Unique among operators because it can take any number of parameters of any types.
Multiple parameters: Ideal for multi-dimensional access like grid(row, col), which is clearer than nested subscripts grid[row][col].
Zero parameters: Can take no parameters, though this is less common and a named member function is usually clearer.
Functors (function objects): The most powerful use of operator() - creates objects that act like functions but maintain state between calls. Each functor instance can have different stored data while providing function-like call syntax.
When to use operator(): Use when you need multiple parameters for indexing, when creating functors that maintain state, or when the operation naturally reads like a function call. Avoid when a named member function would be clearer.
Const correctness: Like other accessors, provide both const and non-const versions when appropriate for different use cases.
The parenthesis operator is most valuable for multidimensional containers and functors, enabling intuitive syntax while maintaining the flexibility to pass multiple parameters or store state.
Function Call Operator Overloading - Quiz
Test your understanding of the lesson.
Practice Exercises
Matrix with Parenthesis Operator
Create a Matrix class that uses the overloaded parenthesis operator for intuitive 2D access using (row, col) syntax. Implement both const and non-const versions for reading and writing.
Lesson Discussion
Share your thoughts and questions
No comments yet. Be the first to share your thoughts!