为二维数组重载运算符[][]。

104
能否重载两次[]运算符?以允许像这样使用:function[3][3](类似于二维数组)。
如果可能的话,我想看一些示例代码。

29
顺便提一句,重载operator()(int, int)通常更简单常见... - Inverse
2
为什么要重复造轮子?只需使用带有范围构造函数的 std::vector:https://dev59.com/U2w05IYBdhLWcg3w41sp#25405865 - Geoffroy
或者你可以直接使用类似于 using array2d = std::array<std::array<int, 3>, 3>; 这样的语句。 - user4640261
17个回答

131

你可以重载operator[],返回一个对象,然后在该对象上再次使用operator[]以获取结果。

class ArrayOfArrays {
public:
    ArrayOfArrays() {
        _arrayofarrays = new int*[10];
        for(int i = 0; i < 10; ++i)
            _arrayofarrays[i] = new int[10];
    }

    class Proxy {
    public:
        Proxy(int* _array) : _array(_array) { }

        int operator[](int index) {
            return _array[index];
        }
    private:
        int* _array;
    };

    Proxy operator[](int index) {
        return Proxy(_arrayofarrays[index]);
    }

private:
    int** _arrayofarrays;
};

那么你可以像这样使用它:

ArrayOfArrays aoa;
aoa[3][5];

这只是一个简单的例子,你可能需要添加许多边界检查和其他操作,但你已经有了想法。


9
需要翻译的内容:could use a destructor. And Proxy::operator[] should return int& not just int可加一个析构函数。而 Proxy::operator[] 应该返回 int& 而不仅仅是 int - Ryan Haining
1
最好使用 std::vector<std::vector<int>> 来避免内存泄漏和复制时的奇怪行为。 - Jarod42
Boostзҡ„multi_arrayе’Ңextent_genйғҪжҳҜиҝҷз§ҚжҠҖжңҜзҡ„еҫҲеҘҪзҡ„дҫӢеӯҗгҖӮhttp://www.boost.org/doc/libs/1_57_0/libs/multi_array/doc/reference.html#extent_gen - alfC
1
然而,const ArrayOfArrays arr; arr[3][5] = 42; 能够通过编译并更改 arr[3][5],这与用户期望的 arrconst 不同。 - abcdabcd987
5
@abcdabcd987,这个观点有几个问题。首先,在这段代码中,Proxy::operator[]没有返回一个引用(假设您的评论不是回复Ryan Haining)。更重要的是,如果arr是const,则无法使用operator[]。您必须定义一个const版本,当然您会让它返回const Proxy。然后,Proxy本身将有const和non-const方法。然后您的示例仍然无法编译,程序员会高兴地发现宇宙万物安好。=) - paddy

24
对于一个二维数组而言,你可能可以使用一个单一的operator[]重载,其返回指向每一行第一个元素的指针。然后,你就可以使用内置的索引运算符来访问行内的每个元素。

5
在我看来,这似乎是最实用和高效的解决方案。不知道为什么它没有得到更多的投票 - 也许是因为它没有引人注目的代码。 - Yigal Reiss
@YigalReiss 或许是因为它没有展示一个例子。可能是这样的 class A { private: int a[Y][X] ; public: int *operator[] (int index) { return a[index]; } }; 使用方式为 A mine ; mine[1][2] = 3; - Déjà vu

22

表达式x[y][z]需要 x[y] 求值为一个支持 d[z] 的对象 d

这意味着,x[y] 应该是一个带有 operator[] 的对象,它会求值为一个“代理对象”,该代理对象也支持 operator[],只有这样才能进行链式调用。

或者,重载 operator() 以接受多个参数,这样您可以调用 myObject(x,y)


为什么使用括号的重载可以获得两个输入,但不能使用方括号做同样的事情? - A. Fenzry
@A.Frenzy 因为:1.使用两个参数的重载将导致调用myObj [2,3],而不是myObj [2] [3]。 2.操作符所需参数的数量无法更改。 []操作符仅需要一个int,而()则可以接受任意类型和数量的参数。 - Scorpio

16

如果在 first [] 调用中返回某种代理类,则可能实现。但是还有另一种选择:您可以重载 operator(),该操作符可以接受任意数量的参数 (function(3,3))。


11

一种方法是使用 std::pair<int,int>

class Array2D
{
    int** m_p2dArray;
public:
    int operator[](const std::pair<int,int>& Index)
    {
       return m_p2dArray[Index.first][Index.second];
    }
};

int main()
{
    Array2D theArray;
    pair<int, int> theIndex(2,3);
    int nValue;
    nValue = theArray[theIndex];
}
当然,您可以使用typedef来定义 pair<int,int>

11
有了 C++11 和花括号初始化,这个变得更加吸引人。现在你可以写成 nValue = theArray[{2,3}]; - Martin Bonner supports Monica

6
您可以使用代理对象,例如:

这样:

#include <iostream>

struct Object
{
    struct Proxy
    {
        Object *mObj;
        int mI;

        Proxy(Object *obj, int i)
        : mObj(obj), mI(i)
        {
        }

        int operator[](int j)
        {
            return mI * j;
        }
    };

    Proxy operator[](int i)
    {
        return Proxy(this, i);
    }
};

int main()
{
    Object o;
    std::cout << o[2][3] << std::endl;
}

5

如果你想说a[x][y]的话,你可以这样写成a[{x,y}]:

struct Coordinate {  int x, y; }

class Matrix {
    int** data;
    operator[](Coordinate c) {
        return data[c.y][c.x];
    }
}

4

如果您能告诉我 functionfunction[x]function[x][y] 是什么,那就太好了。但无论如何,让我们将其视为在某处声明的对象。

SomeClass function;

(因为你说这是运算符重载,所以我认为你不会对像 SomeClass function [16] [32]; 这样的数组感兴趣)

所以functionSomeClass类型的实例。然后查找SomeClass的声明以获取operator []重载的返回类型,就像

ReturnType operator [](ParamType);

然后function [x]将具有ReturnType类型。再次查找ReturnTypeoperator []重载。如果有这样的方法,您可以使用表达式function [x] [y]

请注意,与function(x,y)不同,function [x] [y]是2个单独的调用。因此,除非您在上下文中使用锁定,否则很难保证编译器或运行时的原子性。类似的例子是,libc表示printf是原子的,而连续调用输出流中重载的operator << 则不是。像这样的语句

std::cout << "hello" << std::endl;

可能在多线程应用程序中会存在问题,但类似于以下情况:
printf("%s%s", "hello", "\n");

没问题。


3
template<class F>
struct indexer_t{
  F f;
  template<class I>
  std::result_of_t<F const&(I)> operator[](I&&i)const{
    return f(std::forward<I>(i))1;
  }
};
template<class F>
indexer_t<std::decay_t<F>> as_indexer(F&& f){return {std::forward<F>(f)};}

这让你可以使用 lambda 表达式,生成支持 [] 的索引器。

假设您有一个支持同时传递两个坐标参数的 operator()。现在编写 [][] 支持只需:

auto operator[](size_t i){
  return as_indexer(
    [i,this](size_t j)->decltype(auto)
    {return (*this)(i,j);}
  );
}

auto operator[](size_t i)const{
  return as_indexer(
    [i,this](size_t j)->decltype(auto)
    {return (*this)(i,j);}
  );
}

完成了。无需自定义类。

2
struct test
{
    using array_reference = int(&)[32][32];

    array_reference operator [] (std::size_t index)
    {
        return m_data[index];
    }

private:

    int m_data[32][32][32];
};

我找到了自己简单的解决方案。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接