【问题标题】:Operator[] Overloading in MultiDimensional Arrays c++多维数组中的运算符 [] 重载 c++
【发布时间】:2023-12-07 18:08:01
【问题描述】:

当我打电话时:a7[0][1][100];

我能够获得operator[] 中的第一个索引0,但作为索引我将无法以递归方式获得其他索引值1 和100。我怎么能使用operator[] 来获得递归的以下索引值。在这个 3 维数组的例子中,operator[] 只在第一个维度 0 中被调用一次。

我的示例代码如下:

template <class T, unsigned ... RestD> struct array;

template <class T, unsigned PrimaryD>
struct array <T, PrimaryD> {
    typedef T type[PrimaryD];

    type data;

    T& operator[] (unsigned i) {
        return data[i];
    }
}; 

template <class T, unsigned PrimaryD, unsigned ... RestD>
struct array <T, PrimaryD, RestD...> {
    typedef typename array<T, RestD...>::type OneDimensionDownArrayT;
    typedef OneDimensionDownArrayT type[PrimaryD];

    type data;

    OneDimensionDownArrayT& operator[] (int i) {
        OneDimensionDownArrayT& a = data[i];
        return a;
    }
}; 

int main () {

    array<int, 1, 2, 3> a7 {{{{1, 2, 3},{4, 5, 6}}}};
    a7[0][1][2] = 100; //=>won't recursively go through operator[]
                     //I want to recursively  obtain 0, 1 and 2 as index values

    a7[0][1][100] = 100; //also works correctly.
    std::cout << a7[0][1][100] << std::endl;

    return 0;
}

【问题讨论】:

  • 那么问题出在哪里?你看到了什么行为?如果它给出了编译错误,那是什么?什么是运行时行为?你的编译器和版本是什么?多一点信息也无妨,你知道的!
  • 你有没有考虑过……不这样做?只需使用 operator() 重载即可。真的,人们尝试方式太努力了,无法将operator[] 塞入多维数组中。
  • 问题在于;没有编译或运行时错误。当我调试代码时,在调用 a7[0][1][100] => 时,它只拦截索引“0”的运算符 [],并且不会将其他索引拦截为“1” '和'100'。我只是不会递归地获取我没有使用 operator() 的多维数组中的索引,因为我被限制在 operator[]/ 上实现它
  • 如果你想知道调试器为什么不做你想做的事,你需要问一个关于调试器的问题。看起来程序做了你想让它做的事情。理解所涉及的语言特征没有问题。理解调试器的作用只是一个问题。我说的对吗?

标签: c++ multidimensional-array operator-overloading variadic-templates recursive-datastructures


【解决方案1】:

这里的错误其实有点微妙,换行

   typedef typename array<T, RestD...>::type OneDimensionDownArrayT;

   typedef array<T, RestD...> OneDimensionDownArrayT;

这是因为array&lt;int, 1, 2, 3&gt;::type 等于array&lt;int, 2, 3&gt;::type 等于array&lt;int, 3&gt;::typeint。最后你得到array&lt;int, 1, 2, 3&gt;::OneDimensionDownArrayT 等于int[2][3]。这就是为什么您在重载的 operator[] 中只下降一级的原因,因为它返回一个实际的 int 数组。 您可以通过添加 main 方法自己查看:

        auto & e1 = a7[0];
        auto & e2 = e1[0];
        auto & e3 = e2[1];
        auto & e4 = e3[2];
        e4 = 100;

而不是一次访问它们。然后使用调试器单步执行并检查 e1-e4 的类型。 e1 将具有类型 int (&amp;) [2][3] 而不是 array&lt;int, 2, 3&gt; &amp;

要在堆上而不是在堆栈上分配它们,请在您的类中声明指向 OneDimensionDownArrayT 的指针而不是它们的数组,定义构造函数和析构函数来处理分配/释放数组。它可能看起来像这样:

    template <class T, unsigned PrimaryD, unsigned ... RestD>
    struct array <T, PrimaryD, RestD...> {
        typedef typename array<T, RestD...>::type OneDimensionDownArrayT;

        array():data(new OneDimensionDownArrayT[PrimaryD]){}
        ~array() {
            delete[] data;
        }

        OneDimensionDownArrayT * data;

        OneDimensionDownArrayT& operator[] (int i) {
            OneDimensionDownArrayT& a = data[i];
            return a;
        }
    };

您还需要为您的类定义一个复制构造函数、移动构造函数和赋值运算符。这种实现在堆栈上占用的空间要少得多,但总体上会占用更多的内存,因为指针和数组本身也需要空间。

【讨论】:

  • 此解决方案对于像 a1[1][2][3][4][5][6][7][8][9][10] 这样的非常大的数组效率低下。因为在每个对象内部,它都会创建另一个数组对象,当维度大小增加时,内存使用量会非常大。
  • 无论做什么,都需要分配10个空间!对象。如果堆栈空间不足,请尝试使用堆(动态分配内存)。
  • typedef 数组 OneDimensionDownArrayT;有效,但在它创建的每个维度的对象和数组对象下,它将消耗不必要的内存。有超过 10 个对象,因为它是多维数组。我怎么能动态分配 => typedef array OneDimensionDownArrayT;
  • 10!是十阶乘 = 3628800 个对象。对于 int 数组,这意味着 4 * 3628800 = 14.5MB 的空间。程序通常被编译为具有固定数量的堆栈空间(对我来说大约 8MB,但不能保证这个数字),而堆可以访问所有可用内存。问题是,不仅这个实现需要这么多内存,一个普通的普通int[1][2][3][4][5][6][7][8][9][10] 也将在堆栈上占用 14.5MB。我还更新了我的答案,概述了如何在堆而不是堆栈上分配内存。
【解决方案2】:

如果你想在一个多维数组中连续使用[] 运算符,那么每个[] 必须返回一个(少一维)数组。

如果你的多维数组类型是:

template <class Type, int dim> MArray;

那么MArray&lt;SomeType, n&gt;::operator[] 必须返回MArray&lt;SomeType, n-1&gt;。对于返回对象(最好是引用)的特殊一维数组或返回本机一维数组的二维数组的特殊情况。这个例子使用了过于简单的符号,但关键是一个 n-D []-operator 返回一个 (n-1)-D 数组。

【讨论】:

    最近更新 更多