Skip to Content
Course content

38: Operator Overloading: Subscript and Function Call

Click on the "Edit" button in the top corner of the screen to edit your slide content.

Why would I overload the subscript operator instead of just writing a get() method?

You've probably seen get(int index) or at(int index) in a lot of libraries. They work fine, but they aren't "idiomatic." When you're building a class that acts like a container—say, a custom Matrix class for some graphics work—you want it to feel like a first-class citizen of the language.

Overloading operator[] allows you to use the matrix[i] syntax. It's cleaner, it's faster to type, and more importantly, it makes your class intuitive for other C++ developers. If I see square brackets, I immediately know I'm accessing an element by an index.

struct Matrix {
    int data[100]; // Simplified 1D array representing 10x10

    int& operator[](int index) {
        return data[index];
    }
};

// Now I can do this:
Matrix myMat;
myMat[5] = 42; // Feels just like a native array

Wait, if I want to read a value from a const object, the code above breaks. How do I fix that?

This is a classic "gotcha" I see all the time. In the example above, operator[] returns a reference (int&), which allows you to modify the data. But if you pass your Matrix into a function by const reference, the compiler will scream at you because it can't call a non-const method on a const object.

The solution is to provide two versions of the operator: one for when the object is mutable and one for when it's constant. I usually call this "const overloading."

struct Matrix {
    int data[100];

    // Used for: myMat[i] = 10;
    int& operator[](int index) {
        return data[index];
    }

    // Used for: int x = myMat[i]; (where myMat is const)
    const int& operator[](int index) const {
        return data[index];
    }
};

Notice the const at the end of the second function signature. This tells C++, "this method doesn't change the state of the object." Without both, you're basically telling the compiler your container is either read-only or write-only, which is rarely what you want.

What's the deal with operator(), and why not just use a regular function?

Overloading the function call operator—operator()—turns your object into a "Functor" (a function object). At first glance, it looks like you're just making a class that acts like a function. And that's exactly what you're doing. But here is the secret: unlike a regular function, a functor has state.

Imagine you need a function that adds a specific value to a number, but that value changes based on a configuration file loaded at runtime. A regular function would need a global variable (gross) or a captured variable in a lambda. A functor just stores it as a member variable.

class Adder {
    int amount;
public:
    Adder(int a) : amount(a) {}

    // This makes the object "callable"
    int operator()(int value) const {
        return value + amount;
    }
};

Adder addFive(5);
Adder addTen(10);

int result = addFive(10); // result is 15
int result2 = addTen(10); // result2 is 20

I use these all the time when passing custom logic into STL algorithms like std::transform or std::for_each. It gives you the flexibility of a function with the power of a full-blown class.




📋 Practical Task

Implementing a Bound-Checked Virtual Grid

You need to build a class called VirtualGrid that simulates a 1D array but provides a safe way to access elements. Your task is to implement the following requirements:

  • The class should hold an internal std::vector<int> of a fixed size (initialize it to 10 elements in the constructor).
  • Overload operator[] to allow both reading and writing. However, since operator[] doesn't traditionally throw exceptions in C++, you should simply return 0 (or a default value) if the index is out of bounds for reading, and do nothing if the index is out of bounds for writing.
  • Implement a "Function Call" operator operator(). This operator should take two integers (row and column) and treat the 1D vector as a 2D grid (assuming a width of 5). It should return the value at that coordinate.
  • Ensure that you provide a const version of the subscript operator so the grid can be accessed in read-only contexts.

Test your class by creating a const VirtualGrid and trying to read from it using both [] and ().

Rating
0 0

There are no comments for now.

to be the first to leave a comment.