Multidimensional Subscript Operator in C++
This article provides an overview of the C++ subscript operator
(operator[]), examines its traditional single-argument
constraints, and explores the introduction of native multidimensional
subscripting in recent language standards. Readers will learn how modern
C++ eliminates legacy workarounds—such as proxy objects and the function
call operator—by allowing operator[] to accept multiple
arguments, streamlining custom container and matrix implementations.
What is the Subscript Operator?
The subscript operator, denoted by square brackets [],
provides indexed access to elements within a sequence or collection. In
native C and C++, it is built into raw arrays, where a[i]
is defined as *(a + i).
In object-oriented C++, classes can overload operator[]
to provide intuitive, array-like syntax for custom data structures, such
as std::vector, std::array, and
std::string. Historically, the overloaded operator was
defined as a member function taking exactly one argument:
template <typename T>
class Array {
public:
T& operator[](std::size_t index);
const T& operator[](std::size_t index) const;
};The Historical Limitation: Single-Argument Constraint
Prior to C++23, operator[] was restricted by the
language grammar to accept only a single parameter. If a developer
attempted to pass multiple values separated by commas, such as
matrix[x, y], the compiler interpreted the comma as the
built-in comma operator. This meant the expression x, y
evaluated x, discarded it, and passed only y
to the operator.
To implement multidimensional indexing (such as for a 2D matrix or a 3D tensor), developers had to rely on two primary workarounds:
- The Call Operator: Overloading
operator()instead, allowing syntax likematrix(x, y). While functional, it lacked the idiomatic bracket syntax associated with indexing. - Proxy Objects: Overloading
operator[]to return an intermediate proxy class that also overloadedoperator[], enabling chained access likematrix[x][y]. This approach added boilerplate and could hinder compiler optimizations if not inlined properly.
C++20 paved the way for a solution by officially deprecating the use of the comma operator inside subscript expressions.
Multidimensional Subscripting in C++23
Adopted through proposal P2128R6, C++23 expanded
operator[] to accept zero or more arguments, including
variadic parameter packs. This change aligns the subscript operator's
signature capabilities with the function call operator
(operator()).
Syntax and Implementation
Defining a multidimensional subscript operator requires declaring
operator[] with multiple parameters:
#include <vector>
#include <cstddef>
template <typename T>
class Matrix {
private:
std::vector<T> data;
std::size_t cols;
public:
Matrix(std::size_t rows, std::size_t cols)
: data(rows * cols), cols(cols) {}
// C++23 Multidimensional Subscript Operator
T& operator[](std::size_t row, std::size_t col) {
return data[row * cols + col];
}
const T& operator[](std::size_t row, std::size_t col) const {
return data[row * cols + col];
}
};Usage
With C++23 support enabled, callers can use comma-separated indexes directly within the square brackets:
int main() {
Matrix<int> mat(4, 4);
// Direct multidimensional access
mat[1, 2] = 42;
int value = mat[1, 2];
return 0;
}Variadic Subscripting
The feature also supports variadic templates, enabling arbitrary N-dimensional containers:
template <typename T, std::size_t Dimensions>
class Tensor {
public:
template <typename... Indices>
requires (sizeof...(Indices) == Dimensions)
T& operator[](Indices... indices) {
// Compute flat index and return element
}
};Practical Implications
The introduction of multidimensional subscripting simplifies
mathematical, linear algebra, and data science libraries. It directly
complements modern features such as std::mdspan (introduced
in C++23), a non-owning multidimensional array view that relies on
native multi-argument indexing to provide zero-overhead abstractions for
contiguous and non-contiguous memory layouts.