Array-Style Access Operator
Enable array-style bracket access for your container types.
How Do You Overload the Subscript Operator?
Overloading the subscript operator (operator[]) lets you access elements of your class using array-style bracket syntax, such as myObject[index]. This is commonly used for container classes that store collections of elements.
The subscript operator ([]) allows array-style access to elements. The subscript operator must be overloaded as a member function.
Basic subscript operator overloading
Let's create a Playlist class that manages song indices:
#include <iostream>
class Playlist
{
private:
int m_trackNumbers[10]{};
public:
int& operator[](int index)
{
return m_trackNumbers[index];
}
};
int main()
{
Playlist favorites{};
favorites[0] = 42;
favorites[1] = 17;
favorites[2] = 88;
std::cout << "Track 1: " << favorites[0] << '\n';
std::cout << "Track 2: " << favorites[1] << '\n';
std::cout << "Track 3: " << favorites[2] << '\n';
return 0;
}
The subscript operator returns a reference, allowing us to both read and modify elements using favorites[index] = value syntax.
Why return a reference?
When we write favorites[2] = 88, the expression favorites[2] evaluates first. If operator[] returned by value instead of by reference, we'd get a copy of the track number, and 88 would be assigned to that temporary copy (which would then be discarded). By returning a reference, we get the actual element, and the assignment works correctly.
Const and non-const versions
What if we have a const Playlist? We need both const and non-const versions:
#include <iostream>
class Playlist
{
private:
int m_trackNumbers[10]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
public:
int& operator[](int index)
{
return m_trackNumbers[index];
}
const int& operator[](int index) const
{
return m_trackNumbers[index];
}
};
int main()
{
Playlist myList{};
myList[0] = 99; // Uses non-const version
std::cout << "Track: " << myList[0] << '\n';
const Playlist defaultList{};
// defaultList[0] = 99; // Error: can't modify const object
std::cout << "Default track: " << defaultList[0] << '\n'; // Uses const version
return 0;
}
Adding bounds checking
Unlike built-in arrays, we can add safety checks to our subscript operator:
#include <iostream>
#include <cassert>
#include <cstddef>
class Playlist
{
private:
static constexpr int m_capacity{10};
int m_trackNumbers[m_capacity]{};
public:
int& operator[](int index)
{
assert(index >= 0 && index < m_capacity);
return m_trackNumbers[index];
}
const int& operator[](int index) const
{
assert(index >= 0 && index < m_capacity);
return m_trackNumbers[index];
}
};
int main()
{
Playlist myList{};
myList[0] = 42;
myList[9] = 99;
// myList[10] = 100; // Assertion failure!
std::cout << myList[0] << '\n';
return 0;
}
Using non-integer indices
The subscript operator doesn't have to take an integer parameter. Here's an example using strings as keys:
#include <iostream>
#include <string>
#include <string_view>
#include <vector>
struct GradeEntry
{
std::string subject{};
int grade{};
};
class StudentGrades
{
private:
std::vector<GradeEntry> m_grades{};
public:
int& operator[](std::string_view subject)
{
for (auto& entry : m_grades)
{
if (entry.subject == subject)
return entry.grade;
}
m_grades.push_back(GradeEntry{std::string{subject}, 0});
return m_grades.back().grade;
}
};
int main()
{
StudentGrades student{};
student["Math"] = 95;
student["Science"] = 88;
student["History"] = 92;
std::cout << "Math: " << student["Math"] << '\n';
std::cout << "Science: " << student["Science"] << '\n';
return 0;
}
This program outputs:
Math: 95
Science: 88
Pointers and subscript operators don't mix
Be careful when using subscript operators with pointers:
Playlist* list{new Playlist{}};
// list[0] = 42; // ERROR: treats list as array of Playlist objects
(*list)[0] = 42; // Correct: dereference first, then use subscript
delete list;
Better yet, avoid raw pointers when possible and use objects directly or smart pointers.
Summary
Subscript operator ([]): Must be overloaded as a member function. Enables array-style element access for custom classes.
Return by reference: The subscript operator should return a reference (not a value) to allow both reading and modifying elements using syntax like obj[index] = value.
Const and non-const versions: Provide both T& operator[](int) for modifying elements and const T& operator[](int) const for read-only access to const objects.
Bounds checking: Unlike built-in arrays, overloaded subscript operators can include safety checks using assert() or other validation to catch invalid indices.
Non-integer indices: The subscript operator doesn't require an integer parameter - it can take any type, enabling string-based keys or other index types.
Pointers and subscript operators: Using [] on a pointer to an object applies to the pointer itself (treating it as an array), not the object. Dereference first: (*ptr)[index].
Overloading the subscript operator makes custom container classes work naturally with familiar array-like syntax, with the added benefit of being able to add bounds checking and use non-integer index types.
How did you find this lesson?
Your rating helps us improve the content.
Create an account to track your progress and access interactive exercises. Already have one? Sign in.
Array-Style Access Operator - Quiz
Test your understanding of the lesson.
Practice Exercises
Overloading the Subscript Operator
Implement operator[] for container classes to enable array-like access. Learn to provide both const and non-const versions for read and write access.
Lesson Discussion
Share your thoughts and questions
No comments yet. Be the first to share your thoughts!