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 like matrix(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.