文章目录
- [第一章 C++23语言特性](#第一章 C++23语言特性)
-
- [1.3 多维下标运算符](#1.3 多维下标运算符)
-
- [1.3.1 语法格式](#1.3.1 语法格式)
- [1.3.2 举例实现](#1.3.2 举例实现)
- 1.3.3总结
本文讲解C++23新特性之多维下标运算符
第一章 C++23语言特性
1.3 多维下标运算符
在C++20之前,如果想写一个二维矩阵的元素,通常又以下两种实现方式。
方式1:函数调用风格matrix(x, y)。显示方式是 重载 operator()。缺点是,()这表示一个函数调用,而[] 才代表 访问数据,这不够直观。
方式2:链式下标风格maxtrix[x][y]. 重载 operator[] 返回一个代理对象(Proxy Object),该代理对象再重载 operator[]。这种实现方式需要维护临时对象,难以优化,容易产生性能开销。
C++23 的解决方案: 直接允许 operator[] 像 operator() 一样,接受多个参数。直接使用 matrix[x, y] 这种代码即可实现矩阵元素的访问。
1.3.1 语法格式
语法非常简单,没有任何新关键字,只是放宽了对 operator[] 参数个数的检查。
cpp
struct MyGrid
{
std::vector<int> data;
size_t width;
// C++23 之前:operator[] 只能有一个参数
int & operator[](size_t indices)
{
return data[indices];
}
// C++23 :合法!
int& operator[](size_t x, size_t y)
{
return data[y * width + x];
}
// 支持变长参数模板
template<typename... Args>
int& operator[](Args... indices)
{
// 计算多维偏移量
}
};
void test()
{
MyGrid grid;
grid[2, 3] = 33;
}
注意在MSVC中需要设置编译器支持:最新 C++ 工作草案中的功能 (/std:c++latest).
1.3.2 举例实现
示例1:基础二维数组实现
实现方式如下:
cpp
class Matrix2D
{
public:
Matrix2D(size_t r, size_t c)
: rows(r)
, cols(c)
, data(r* c)
{
}
// C++23 多维度下的重载
double& operator[](size_t row, size_t col)
{
return data[row * cols + col];
}
// const版本
const double& operator[](size_t row, size_t col) const
{
return data[row * cols + col];
}
private:
std::vector<double> data;
size_t rows, cols;
};
void test()
{
Matrix2D mat(4, 4);
mat[1, 2] = 3.14;
std::cout << "mat[1,2]: " << mat[1, 2] << std::endl;
}
这在最新的vs2026下编译不过,还不支持这样的实现方式。
示例2:变长参数(N维张量)
结合变长模板参数(Variadic Templates),我们可以创建一个支持任意维度的张量类。
实现了一个类似python中的张量。
cpp
template<size_t N>
struct Tensor
{
private:
std::array<size_t, N> shape_; // 存储每个维度的尺寸,例如 {2, 3, 4}
std::array<size_t, N> strides_; // 存储每个维度的步长
std::vector<int> data_; // 存储数据的一维数组
// 辅助函数,用于计算一维索引
template<typename... Args>
size_t get_index(size_t i, Args... rest) const
{
// 递归计算:当前维度的索引 * 当前维度的步长 + 剩余维度的计算结果
constexpr size_t dim = N - sizeof...(rest) - 1;
return shape_[dim] > i ? (i * strides_[dim] + get_index(rest...)) : 0;
}
// 递归基例:最后一个维度
size_t get_index(size_t i) const
{
constexpr size_t dim = N - 1;
return shape_[dim] > i ? i * strides_[dim] : 0;
}
public:
// 构造函数,接收每个维度的尺寸
template<typename... Dims>
Tensor(Dims... dims) : shape_{ static_cast<size_t>(dims)... }
{
static_assert(sizeof...(Dims) == N, "Initializer list does not match number of dimensions");
size_t total_size = 1;
// 从后向前计算步长和总大小
for (int i = N - 1; i >= 0; --i)
{
strides_[i] = total_size;
total_size *= shape_[i];
}
data_.resize(total_size);
}
// C++23 多维下标运算符
template<typename... Args>
int& operator[](Args... indices)
{
static_assert(sizeof...(Args) == N, "Wrong number of dimensions for subscript");
return data_[get_index(indices...)];
}
// const 版本
template<typename... Args>
const int& operator[](Args... indices) const
{
static_assert(sizeof...(Args) == N, "Wrong number of dimensions for subscript");
return data_[get_index(indices...)];
}
};
void test()
{
// 创建一个 2x3x4 的三维张量
Tensor<3> t(2, 3, 4);
// 赋值
t[1, 2, 3] = 42;
t[0, 1, 2] = 100;
// 读取
std::cout << "t[1, 2, 3] = " << t[1, 2, 3] << std::endl;
std::cout << "t[0, 1, 2] = " << t[0, 1, 2] << std::endl;
// 尝试使用错误的维度数量,会触发 static_assert 编译错误
// t[0, 0] = 1; // 错误: "Wrong number of dimensions for subscript"
}
1.3.3总结
C++23支持的多维度下标,相比 [][] 的代理对象模式,[x, y] 是单次函数调用,更容易被编译器内联和优化。