Multidimensional Subscript Operator in C++23
C++23 introduces the multidimensional subscript operator, a core
language enhancement that enables operator[] to accept
multiple arguments. Prior to this release, developers building
multidimensional data structures like matrices and tensors were forced
to use call operators () or complex proxy objects to chain
single-index brackets ([][]). With C++23, classes can
natively implement matrix indexing using standard array subscript
syntax, such as matrix[row, col], dramatically improving
code readability, ergonomics, and performance consistency.
The Limitation Before C++23
Historically, the subscript operator operator[] in C++
was restricted to accepting exactly one argument. When implementing
multi-axis structures such as matrices, developers had to rely on
several sub-optimal workarounds:
- Function Call Operator
operator(): Syntax likematrix(row, col)was the most common convention, though it syntactically mimics function invocation rather than element access. - Chained Subscripts via Proxy Objects: Implementing
matrix[row][col]required the first[]to return an intermediate lightweight view (proxy object) representing a row, which in turn evaluated the second[]. This added implementation complexity and could sometimes impede compiler optimization. - One-Dimensional Indexing: Flattening indices
manually with expressions like
matrix[row * cols + col], which reduced expressiveness and increased error potential.
How the C++23 Multidimensional Subscript Operator Works
Under proposal P2128R6, C++23 lifted the single-parameter restriction
on operator[]. Classes can now define
operator[] with zero, two, or more arguments, including
variadic parameter packs.
Inside the brackets, arguments are passed as a comma-separated list without requiring an extra set of parentheses:
#include <vector>
#include <cstddef>
#include <iostream>
template <typename T>
class Matrix {
private:
std::size_t rows_;
std::size_t cols_;
std::vector<T> data_;
public:
Matrix(std::size_t rows, std::size_t cols)
: rows_(rows), cols_(cols), data_(rows * 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];
}
};
int main() {
Matrix<int> mat(3, 3);
// Direct multi-index assignment and access
mat[0, 1] = 42;
mat[2, 2] = 99;
std::cout << "mat[0, 1]: " << mat[0, 1] << '\n';
std::cout << "mat[2, 2]: " << mat[2, 2] << '\n';
}Comma Operator Deprecation in Subscripts
To enable this syntax without ambiguity, C++20 previously deprecated
the use of the comma operator inside subscript expressions (e.g.,
a[x, y] previously evaluated x, discarded it,
and accessed a[y]). In C++23, comma-separated expressions
inside [] are parsed exclusively as separate arguments to
operator[].
Integration with
std::mdspan
The primary driver for this language change was the addition of
std::mdspan in C++23. Standard library multidimensional
spans act as non-owning, multi-dimensional views over contiguous
sequences. Using the multidimensional subscript operator,
std::mdspan provides uniform and high-performance
access:
#include <mdspan>
#include <vector>
void process_grid() {
std::vector<double> buffer(100);
// View the 1D buffer as a 10x10 2D matrix
std::mdspan grid(buffer.data(), 10, 10);
grid[3, 4] = 3.14159;
}Key Advantages
- Syntactic Consistency: Restores array-like bracket syntax for higher-dimensional arrays and mathematical matrices.
- Zero Overhead: Eliminates the need for temporary row-proxy structures, simplifying inlining and register allocation for the optimizer.
- Variadic Support: Enables generic programming where rank (number of dimensions) can be an arbitrary compile-time parameter.