Operator Overloading

Subscript and Function Call Operators

Subscript and Function Call Operators

The subscript operator [] and the function call operator () allow a class object to behave like an array or like a function, respectively. Both operators are implemented as member functions. Both also allow for arguments and return any type and overload so long as that combination is appropriate for the type they mimic.

The subscript operator, This operator is useful in situations in which a class object is indexable. Typically, the operator accepts one argument (an int index) and returns a reference to the element in that index, which can be used to either set a new value in that element or to retrieve an existing one. The following shows one class with two operator[]s implemented, one for const object references and one for regular object references:

#include <cstring>

class IntArray
{
private:
    int* m_data;

public:
    IntArray()
        : m_data{ new int[5]{} }
    {
    }

    IntArray(const IntArray& arr)
        : m_data{ new int[5] }
    {
        // Call this function with the provided arguments
        std::memcpy(m_data, arr.m_data, sizeof(int) * 5);
    }

    ~IntArray()
    {
        delete[] m_data;
    }

    int& operator[](int i)
    {
        return m_data[i];
    }

    const int& operator[](int i) const
    {
        return m_data[i];
    }
};

The expression arr[0] = 10 in non const code becomes an internal call to IntArray::operator[](0), which returns an int&. Because it’s a reference, you actually modify the contents of m_data[0]. The const version of operator[] returns a const int&, thus letting you read a const IntArray, but not modify it.

The compiler will make the right call depending on whether arr is a const object.

We’re omitting range checked indexing in the implementation simply to keep the code concise. In a full container, there usually should be boundary check.

Function call operator. This operator turns an object into one you can call like a function. As with the subscript operator, this function can receive any arguments it wants and can return whatever makes sense to be returned from a ‘function call’:

class AddFiveObject
{
private:
    int m_value;

public:
    AddFiveObject()
        : m_value{ 0 }
    {
    }

    AddFiveObject(int value)
        : m_value{ value }
    {
    }

    int operator()(int i)
    {
        // Return the computed value to the caller
        return i + m_value;
    }

    const int operator()(int first, int second)
    {
        return first + second + m_value;
    }
};

add5(10) becomes an inside compiler call to add5.operator()(10). The function AddFiveObject::operator()(10) calculates and then returns 10 + m_value. Second argument version allows to pass some ‘configuration’.

‘Function’ objects, sometimes known as function objects or functors, is a term coined to describe this idea: an object that ‘behaves like a function.’

You will find this pattern commonly employed in standard library algorithms, for example.

Why would anyone bother doing this? Well, a function call that is associated with an object can carry its own data with it, giving it memory, whereas free functions and standalone functions can only hold current process memory. Like the subscripting operator, the operator() function that supports these calls must be defined as a member function. In a more complete scenario, any data needed for the ‘call’ would be passed in to the constructor, giving it persistent memory until the function object expires.

Exercise: Subscript and Function Call OperatorsStarter code, hints and solutionOpenClose
Guided practiceBeginner · 10–15 min

Practice: Subscript and Function Call Operators

Work through the mission, reveal hints only when needed, and compare your result with the model solution.

Your challenge

Add calls using scores[2] and double_value(6). Predict their results and keep the subscript within the three stored elements.

Mission checklist

  1. Run the starter as a baseline, then predict the change requested in Your challenge.
  2. Make that change in your editor; use the lesson-specific hints if you need help.
  3. Compile and run your answer, then compare with the worked solution and explain the result.

Starter program

This is the working baseline, not the completed answer. Compile as C++20, then make the change described above.

#include <iostream>
#include <vector>

class Scores
{
private:
    std::vector<int> values{ 7, 9, 8 };

public:
    int operator[](std::size_t index) const
    {
        return values[index];
    }
};

class Doubler
{
public:
    int operator()(int value) const
    {
        return value * 2;
    }
};

int main()
{
    Scores scores;
    Doubler double_value;
    std::cout << scores[1] << ' ' << double_value(5) << '\n';
    return 0;
}

Hints

Hint 1

scores[1] calls scores.operator[](1); double_value(5) calls double_value.operator()(5). The brackets choose an element, while the parentheses invoke a calculation.

Hint 2

Only indexes 0, 1, and 2 are valid for this Scores object. Change the index within that range and separately change the Doubler argument to see the two operations independently.

Worked solution

The added calls use the third score and a new doubling argument. scores[2] returns 8, and double_value(6) returns 12. Index 2 remains within the three-element vector.

#include <iostream>
#include <vector>

class Scores
{
private:
    std::vector<int> values{ 7, 9, 8 };

public:
    int operator[](std::size_t index) const
    {
        return values[index];
    }
};

class Doubler
{
public:
    int operator()(int value) const
    {
        return value * 2;
    }
};

int main()
{
    Scores scores;
    Doubler double_value;
    std::cout << scores[1] << ' ' << double_value(5) << '\n';
    std::cout << "New calls: " << scores[2] << ' ' << double_value(6) << '\n';
    return 0;
}

Example output

9 10
New calls: 8 12

0 comments

No comments yet. Be the first.

Add a comment

About CppLearn

CppLearn is an interactive, free reference for modern C++. In each lesson, you'll find out how the language works, rather than just lines of code that you can copy and paste. We cover concepts starting from beginner lessons on fundamentals and proceed to templates, move semantics, smart pointers, and areas that usually trap or scare C++ learners.