- Subscript Operator Reference (cppreference)
std::mdspanReference (cppreference)- ISO/IEC 14882 C++ Standard Sections:
[over.sub],[class.sub]
The Multidimensional Subscript Operator (operator[]) is a C++23 language feature (introduced via proposals P2120R0 and P2576R0) that allows the array subscript operator operator[] to accept multiple parameters, zero parameters, or variadic parameter packs (matrix[row, col]). By permanently replacing pre-C++23 proxy row chaining (m[r][c]) and function call operator overloading (m(r, c)), multidimensional subscripting provides native, type-safe array indexing for matrices, tensors, image buffers, and C++23's non-owning multidimensional array views (std::mdspan).
For over 40 years (inherited from C in 1972 and C++98), operator[] was constrained by the C++ grammar to accept strictly one parameter: T& operator[](std::size_t index).
When building multi-dimensional data structures (2D matrices, 3D spatial grids, image arrays), developers were forced into three flawed workarounds:
// Pre-C++23 Workaround 1: Chained Single-Index Proxy Objects
matrix[row][col]; // Requires operator[](row) to return a temporary ProxyRow object!
// Pre-C++23 Workaround 2: Function Call Operator Overloading
matrix(row, col); // Obscured intent! Looked like a function call, NOT array element access!- Proxy Object Chaining Overhead (
m[r][c]): Indexing a 2D matrix asm[r][c]requiredoperator[](r)to construct a temporaryProxyRowobject, which then declared its ownoperator[](c). This created compiler inline barriers, register pressure, and pointer arithmetic overhead. - Syntactic Domain Mis-association (
m(r, c)): Overloadingoperator()(r, c)allowed passing multiple indices, butm(r, c)syntactically resembled function invocation rather than container element indexing. - The Comma Operator Trap: In pre-C++20 C++, writing
m[1, 2]evaluated1, 2as a standard C comma expression (evaluating1, discarding1, and passing2as the single index argument tom.operator[](2)!). C++20 proposal P1161R3 explicitly deprecated comma expressions inside[]to reclaim the syntax for C++23 multidimensional subscripting.
Solution: C++20 deprecated comma expressions inside brackets, and C++23 updated [over.sub], permitting operator[] to take arbitrary parameter lists (matrix[r, c] or tensor[x, y, z, w]).
In C++23, operator[] can be overloaded with any number of parameters:
struct Matrix3D {
// C++23 Multidimensional Subscript Operator
double& operator[](std::size_t x, std::size_t y, std::size_t z);
// C++23 Variadic Subscript Operator Template
template <typename... Indices>
double& operator[](Indices... indices);
};The primary C++23 Standard Library feature enabled by multidimensional operator[] is std::mdspan (<mdspan>, proposal P0009R18). std::mdspan is a non-owning
std::mdspan<double, std::extents<3, 3>>
┌─────────────────────────────────────────────────────────────────────────────────────────┐
│ Contiguous Memory Buffer: [ 0.0, 1.0, 2.0 | 3.0, 4.0, 5.0 | 6.0, 7.0, 8.0 ] │
└───────────────────────────────────────────┬─────────────────────────────────────────────┘
│
▼
grid[row, col] Indexing
grid[1, 2] === 5.0
┌──────────────────────────────────────────────────────────────────┐
│ C++11 / C++14 / C++17 │
├──────────────────────────────────────────────────────────────────┤
│ • operator[] strictly limited to 1 argument │
│ • Multidimensional indexing required m[r][c] proxies or m(r, c) │
└──────────────────────────────────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────────────┐
│ C++20 │
├──────────────────────────────────────────────────────────────────┤
│ • Deprecated comma operator inside [] (P1161R3) │
│ • Reclaimed bracket syntax for multidimensional subscripting │
└──────────────────────────────────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────────────┐
│ C++23 │
├──────────────────────────────────────────────────────────────────┤
│ • Introduced Multidimensional Subscript Operator (P2120R0) │
│ • Introduced std::mdspan in <mdspan> (P0009R18) │
│ • Supported zero, two, or N variadic parameters in operator[] │
└──────────────────────────────────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────────────┐
│ C++26 │
├──────────────────────────────────────────────────────────────────┤
│ • C++26 Pack Indexing over variadic subscript parameter packs │
└──────────────────────────────────────────────────────────────────┘
Demonstrates implementing a 2D matrix class with C++23 multidimensional subscript indexing.
#include <iostream>
#include <vector>
#include <cstddef>
template <typename T>
class Matrix2D {
private:
std::size_t m_rows;
std::size_t m_cols;
std::vector<T> m_data;
public:
Matrix2D(std::size_t rows, std::size_t cols)
: m_rows(rows), m_cols(cols), m_data(rows * cols) {}
// C++23 Multidimensional Subscript Operator (Non-Const Lvalue)
T& operator[](std::size_t r, std::size_t c) noexcept {
return m_data[r * m_cols + c]; // Calculates 2D row-major offset
}
// C++23 Multidimensional Subscript Operator (Const Lvalue)
const T& operator[](std::size_t r, std::size_t c) const noexcept {
return m_data[r * m_cols + c];
}
};
int main() {
Matrix2D<int> matrix(3, 3);
// C++20 Deprecated m[0,0] is now VALID C++23 Multidimensional Subscripting!
matrix[0, 0] = 10;
matrix[1, 2] = 42;
matrix[2, 1] = 99;
std::cout << "Matrix[1, 2]: " << matrix[1, 2] << " (Expected: 42)\n";
std::cout << "Matrix[2, 1]: " << matrix[2, 1] << " (Expected: 99)\n";
return 0;
}Demonstrates implementing a variadic operator[] that accepts arbitrary
#include <iostream>
#include <array>
#include <numeric>
template <typename T, std::size_t... Dimensions>
class Tensor {
private:
static constexpr std::size_t TotalSize = (Dimensions * ... * 1);
std::array<T, TotalSize> m_data{};
// Helper calculating linear index from variadic coordinate pack
template <typename... Indices>
constexpr std::size_t calculate_offset(Indices... indices) const noexcept {
std::size_t offset = 0;
std::size_t stride = 1;
std::size_t idx_array[] = { static_cast<std::size_t>(indices)... };
std::size_t dim_array[] = { Dimensions... };
for (std::size_t i = sizeof...(Dimensions); i > 0; --i) {
offset += idx_array[i - 1] * stride;
stride *= dim_array[i - 1];
}
return offset;
}
public:
// C++23 Variadic Multidimensional Subscript Operator
template <typename... Indices>
requires (sizeof...(Indices) == sizeof...(Dimensions))
constexpr T& operator[](Indices... indices) noexcept {
return m_data[calculate_offset(indices...)];
}
};
int main() {
// 3D Tensor with dimensions 2x3x4
Tensor<int, 2, 3, 4> tensor3d;
tensor3d[1, 2, 3] = 777; // C++23 3D Subscripting!
std::cout << "3D Tensor[1, 2, 3]: " << tensor3d[1, 2, 3] << " (Expected: 777)\n";
return 0;
}Demonstrates using standard <mdspan> for zero-copy 2D memory indexing.
#include <iostream>
#include <vector>
// C++23 <mdspan> Header
#if defined(__cpp_lib_mdspan) && __cpp_lib_mdspan >= 202302L
#include <mdspan>
int main() {
std::vector<int> raw_buffer = {1, 2, 3, 4, 5, 6}; // 2x3 contiguous grid
// C++23 std::mdspan: Non-owning 2D view over 1D vector (2 rows, 3 columns)
std::mdspan<int, std::extents<std::size_t, 2, 3>> grid(raw_buffer.data());
// Multidimensional subscript indexing directly on std::mdspan!
std::cout << "Grid[0, 2]: " << grid[0, 2] << " (Expected: 3)\n";
std::cout << "Grid[1, 1]: " << grid[1, 1] << " (Expected: 5)\n";
return 0;
}
#else
int main() {
std::cout << "C++23 <mdspan> multidimensional view demonstrated.\n";
return 0;
}
#endif-
Use
matrix[r, c]for All New Multidimensional Containers:- In C++23 and later, adopt
matrix[r, c]for 2D/3D grids, images, and spatial data structures. It communicates array indexing semantics better thanmatrix(r, c).
- In C++23 and later, adopt
-
Prefer
std::mdspanfor Non-Owning Matrix Views:- Rather than creating custom matrix wrapper classes to view raw memory buffers, use standard
std::mdspan(<mdspan>), which provides layout policies (layout_left,layout_right,layout_stride) out of the box.
- Rather than creating custom matrix wrapper classes to view raw memory buffers, use standard
-
C++20 Legacy Code Migration Hazard:
- If legacy pre-C++20 code contained comma operator expressions inside brackets (
arr[a, b]intended asb), updating to C++23 will route the call to multidimensionaloperator[]. Wrap comma expressions in parentheses (arr[(a, b)]) if the C comma operator behavior was intended.
- If legacy pre-C++20 code contained comma operator expressions inside brackets (
- Related to:
std::mdspan(C++23 P0009R18) — Standard multidimensional non-owning array view.- Variadic Templates — Underpins variadic
operator[](Indices...)parameters. - Operator Overloading (
operator[]) — Base subscript operator grammar.
- Superseded by: None.
- Supersedes: Chained single-index proxy objects (
m[r][c]), function call overloading (m(r, c)), and single-index linear offset formulas.
Back to C++23 section.