C++中自定义N维数组operator[]的高效实现(使用可变参数模板)
高效实现N维数组切片
operator[]的方案 我在学习可变参数模板、研究boost、xTensor等N维数组库的过程中,需要实现operator[](size_t i),让它返回原数组在索引i处的(N-1)维切片。现有两个思路都存在缺陷:
- 思路1:递归定义子数组对象,
operator[]移动指针返回引用,效率极高,但同一表达式多次调用会导致多个子数组指向同一数据,引发逻辑问题。 - 思路2:使用子数组指针数组,首次访问时动态分配,后续复用,解决了共享问题但效率低下,本质和嵌套
vector类似。
最优解决方案:轻量级视图(View)类
核心思路是用不持有数据的视图类包装原数组的指针,每次调用operator[]返回新的视图实例——既保留思路1的高效性,又避免了共享子数组的问题。
视图类仅存储指向原数据的指针,所有维度计算在编译期完成,创建和拷贝视图的开销为O(1),完全没有动态内存分配的额外消耗。
具体实现代码
#include <algorithm> #include <type_traits> // 1维视图特化 template <size_t Dim> class ArrayView1D { public: static constexpr size_t N = 1; explicit ArrayView1D(double* data) : data_(data) {} double& operator[](size_t i) { return data_[i]; } const double& operator[](size_t i) const { return data_[i]; } double* data() { return data_; } const double* data() const { return data_; } private: double* data_; }; // N维视图(N>1) template <size_t FirstDim, size_t... RestDims> class ArrayViewND { public: static constexpr size_t N = sizeof...(RestDims) + 1; static constexpr size_t slice_size = (RestDims * ...); // 单个切片的元素总数 explicit ArrayViewND(double* data) : data_(data) {} // 返回对应索引的(N-1)维视图 ArrayViewND<RestDims...> operator[](size_t i) { return ArrayViewND<RestDims...>(data_ + i * slice_size); } double* data() { return data_; } const double* data() const { return data_; } private: double* data_; }; // 统一视图接口的别名 template <size_t... Dims> using ArrayView = std::conditional_t<(sizeof...(Dims) == 1), ArrayView1D<Dims...>, ArrayViewND<Dims...>>; // N维数组容器(N>1) template <size_t FirstDim, size_t... RestDims> class Array { private: static constexpr size_t N = sizeof...(RestDims) + 1; static constexpr size_t length = FirstDim * (RestDims * ...); static constexpr size_t Dims[] = {FirstDim, RestDims...}; double* data_; public: Array() { data_ = new double[length]; std::fill_n(data_, length, 0.0); } Array(const Array& other) { data_ = new double[length]; std::copy(other.data_, other.data_ + length, data_); } Array(Array&& other) noexcept : data_(other.data_) { other.data_ = nullptr; } ~Array() { delete[] data_; // 修正原代码错误:new[]分配的内存需用delete[]释放 } Array& operator=(const Array& other) { if (this != &other) { delete[] data_; data_ = new double[length]; std::copy(other.data_, other.data_ + length, data_); } return *this; } Array& operator=(Array&& other) noexcept { if (this != &other) { delete[] data_; data_ = other.data_; other.data_ = nullptr; } return *this; } // 返回完整N维视图 ArrayView<FirstDim, RestDims...> view() { return ArrayView<FirstDim, RestDims...>(data_); } // 直接支持切片操作,返回(N-1)维视图 ArrayView<RestDims...> operator[](size_t i) { return view()[i]; } double* data() { return data_; } const double* data() const { return data_; } }; // 1维数组容器特化 template <size_t Dim> class Array<Dim> { private: static constexpr size_t length = Dim; double* data_; public: Array() { data_ = new double[length]; std::fill_n(data_, length, 0.0); } Array(const Array& other) { data_ = new double[length]; std::copy(other.data_, other.data_ + length, data_); } Array(Array&& other) noexcept : data_(other.data_) { other.data_ = nullptr; } ~Array() { delete[] data_; } Array& operator=(const Array& other) { if (this != &other) { delete[] data_; data_ = new double[length]; std::copy(other.data_, other.data_ + length, data_); } return *this; } Array& operator=(Array&& other) noexcept { if (this != &other) { delete[] data_; data_ = other.data_; other.data_ = nullptr; } return *this; } double& operator[](size_t i) { return data_[i]; } const double& operator[](size_t i) const { return data_[i]; } double* data() { return data_; } const double* data() const { return data_; } };
方案优势
- 极致高效:视图仅持有一个指针,所有维度计算在编译期完成,创建、拷贝视图的开销可以忽略,性能接近思路1。
- 无共享问题:每次调用
operator[]都会生成新的视图实例,多个视图指向同一数据但彼此独立,不会出现子数组对象共享导致的逻辑错误。 - 无额外内存开销:不需要预分配子数组指针数组,完全避免了思路2的动态分配成本。
使用示例
int main() { // 定义一个2×3×4的3维数组 Array<2,3,4> arr; // 链式访问并修改元素 arr[0][1][0] = 1.0; // 同时获取两个独立切片,互不干扰 auto slice1 = arr[0]; auto slice2 = arr[1]; slice1[0][0] = 2.0; slice2[0][0] = 3.0; return 0; }
内容的提问来源于stack exchange,提问作者user20454015
相关产品推荐
相关产品推荐

