You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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. 极致高效:视图仅持有一个指针,所有维度计算在编译期完成,创建、拷贝视图的开销可以忽略,性能接近思路1。
  2. 无共享问题:每次调用operator[]都会生成新的视图实例,多个视图指向同一数据但彼此独立,不会出现子数组对象共享导致的逻辑错误。
  3. 无额外内存开销:不需要预分配子数组指针数组,完全避免了思路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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.31 15:10:33