运算符[] []重载


90

是否可能[]两次重载运算符?允许的是这样的:(function[3][3]例如在二维数组中)。

如果可能的话,我想看一些示例代码。


22
顺便说一句,operator()(int, int)相反,重载更简单和更常见了……

2
为什么要重新制作轮子?只需std::vector与范围构造函数一起使用:stackoverflow.com/a/25405865/610351
Geoffroy 2014年

或者,您可以使用类似using array2d = std::array<std::array<int, 3>, 3>;
adem

Answers:


118

您可以重载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];

这只是一个简单的示例,您想添加一堆边界检查和内容,但是您明白了。


5
可以使用析构函数。而且Proxy::operator[]应该返回int&不仅仅是int
瑞安海宁

1
最好使用它std::vector<std::vector<int>>来避免复制时出现内存泄漏和奇怪的行为。
Jarod42

Boost multi_arrayextent_gen都是该技术的很好例子。boost.org/doc/libs/1_57_0/libs/multi_array/doc/…–
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和非const方法。然后您的示例仍然无法编译,程序员将很高兴宇宙中的一切都很好。=)
水稻

21

表达式x[y][z]需要x[y]计算为d支持的对象d[z]

这意味着x[y]应该是一个对象,其带有operator[],其结果为支持的“代理对象” operator[]

这是链接它们的唯一方法。

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


为什么括号的重载允许获得两个输入,但括号却不能相同?
A.疯狂

19

特别是对于二维数组,您可能会避免使用单个operator []重载而返回指向每一行第一个元素的指针。

然后,您可以使用内置的索引操作符来访问行中的每个元素。


4
在我看来,这是最实用,最有效的解决方案。想知道为什么它没有获得更多的选票-也许是因为它没有醒目的代码。
Yigal Reiss

16

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


9

一种方法是使用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];
}

当然,你可能typedefpair<int,int>


8
这成为了很多与C ++ 11和支柱初始化更具吸引力。现在您可以写作nValue = theArray[{2,3}];
Martin Bonner

5

您可以使用代理对象,如下所示:

#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;
}

4

这会是巨大的,如果你可以让我知道什么functionfunction[x]function[x][y]是。但是无论如何,我还是将其视为声明为

SomeClass function;

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

所以function是type的实例SomeClass。然后查找重载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");

很好


2
#include<iostream>

using namespace std;

class Array 
{
     private: int *p;
     public:
          int length;
          Array(int size = 0): length(size)
          {
                p=new int(length);
          }
          int& operator [](const int k)
          {
               return p[k];
          }
};
class Matrix
{
      private: Array *p;
      public: 
            int r,c;
            Matrix(int i=0, int j=0):r(i), c(j)
            {
                 p= new Array[r];
            }
            Array& operator [](const int& i)
            {
                 return p[i];
            }
};

/*Driver program*/
int main()
{
    Matrix M1(3,3); /*for checking purpose*/
    M1[2][2]=5;
}

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];
};

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


2
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()支持在onxe传递两个坐标作为两个参数的。现在编写[][]支持只是:

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);}
  );
}

并做了。不需要自定义类。



1

使用专门的模板处理程序可以重载多个[]。只是为了展示它是如何工作的:

#include <iostream>
#include <algorithm>
#include <numeric>
#include <tuple>
#include <array>

using namespace std;

// the number '3' is the number of [] to overload (fixed at compile time)
struct TestClass : public SubscriptHandler<TestClass,int,int,3> {

    // the arguments will be packed in reverse order into a std::array of size 3
    // and the last [] will forward them to callSubscript()
    int callSubscript(array<int,3>& v) {
        return accumulate(v.begin(),v.end(),0);
    }

};

int main() {


    TestClass a;
    cout<<a[3][2][9];  // prints 14 (3+2+9)

    return 0;
}

现在定义SubscriptHandler<ClassType,ArgType,RetType,N>使先前的代码起作用。它仅显示如何完成。此解决方案既不是最佳解决方案,也不是没有错误(例如,不是线程安全的)。

#include <iostream>
#include <algorithm>
#include <numeric>
#include <tuple>
#include <array>

using namespace std;

template <typename ClassType,typename ArgType,typename RetType, int N> class SubscriptHandler;

template<typename ClassType,typename ArgType,typename RetType, int N,int Recursion> class SubscriptHandler_ {

    ClassType*obj;
    array<ArgType,N+1> *arr;

    typedef SubscriptHandler_<ClassType,ArgType,RetType,N,Recursion-1> Subtype;

    friend class SubscriptHandler_<ClassType,ArgType,RetType,N,Recursion+1>;
    friend class SubscriptHandler<ClassType,ArgType,RetType,N+1>;

public:

    Subtype operator[](const ArgType& arg){
        Subtype s;
        s.obj = obj;
        s.arr = arr;
        arr->at(Recursion)=arg;
        return s;
    }
};

template<typename ClassType,typename ArgType,typename RetType,int N> class SubscriptHandler_<ClassType,ArgType,RetType,N,0> {

    ClassType*obj;
    array<ArgType,N+1> *arr;

    friend class SubscriptHandler_<ClassType,ArgType,RetType,N,1>;
    friend class SubscriptHandler<ClassType,ArgType,RetType,N+1>;

public:

    RetType operator[](const ArgType& arg){
        arr->at(0) = arg;
        return obj->callSubscript(*arr);
    }

};


template<typename ClassType,typename ArgType,typename RetType, int N> class SubscriptHandler{

    array<ArgType,N> arr;
    ClassType*ptr;
    typedef SubscriptHandler_<ClassType,ArgType,RetType,N-1,N-2> Subtype;

protected:

    SubscriptHandler() {
        ptr=(ClassType*)this;
    }

public:

    Subtype operator[](const ArgType& arg){
        Subtype s;
        s.arr=&arr;
        s.obj=ptr;
        s.arr->at(N-1)=arg;
        return s;
    }
};

template<typename ClassType,typename ArgType,typename RetType> struct SubscriptHandler<ClassType,ArgType,RetType,1>{
    RetType operator[](const ArgType&arg) {
        array<ArgType,1> arr;
        arr.at(0)=arg;
        return ((ClassType*)this)->callSubscript(arr);
    }
};

0

使用std::vector<std::vector<type*>>,您可以使用自定义输入运算符构建内部向量,该运算符将遍历数据并返回指向每个数据的指针。

例如:

size_t w, h;
int* myData = retrieveData(&w, &h);

std::vector<std::vector<int*> > data;
data.reserve(w);

template<typename T>
struct myIterator : public std::iterator<std::input_iterator_tag, T*>
{
    myIterator(T* data) :
      _data(data)
    {}
    T* _data;

    bool operator==(const myIterator& rhs){return rhs.data == data;}
    bool operator!=(const myIterator& rhs){return rhs.data != data;}
    T* operator*(){return data;}
    T* operator->(){return data;}

    myIterator& operator++(){data = &data[1]; return *this; }
};

for (size_t i = 0; i < w; ++i)
{
    data.push_back(std::vector<int*>(myIterator<int>(&myData[i * h]),
        myIterator<int>(&myData[(i + 1) * h])));
}

现场例子

该解决方案的优点是为您提供了一个真正的STL容器,因此您可以使用特殊的for循环,STL算法等。

for (size_t i = 0; i < w; ++i)
  for (size_t j = 0; j < h; ++j)
    std::cout << *data[i][j] << std::endl;

但是,它确实会创建指针向量,因此,如果您使用的是这样的小型数据结构,则可以直接在数组内部复制内容。


0

样例代码:

template<class T>
class Array2D
{
public:
    Array2D(int a, int b)  
    {
        num1 = (T**)new int [a*sizeof(int*)];
        for(int i = 0; i < a; i++)
            num1[i] = new int [b*sizeof(int)];

        for (int i = 0; i < a; i++) {
            for (int j = 0; j < b; j++) {
                num1[i][j] = i*j;
            }
        }
    }
    class Array1D
    {
    public:
        Array1D(int* a):temp(a) {}
        T& operator[](int a)
        {
            return temp[a];
        }
        T* temp;
    };

    T** num1;
    Array1D operator[] (int a)
    {
        return Array1D(num1[a]);
    }
};


int _tmain(int argc, _TCHAR* argv[])
{
    Array2D<int> arr(20, 30);

    std::cout << arr[2][3];
    getchar();
    return 0;
}

0

只有在行的长度可变且在内存使用/分配方面效率太低的情况下才需要使用vector <vector <T>>或T **,如果需要矩形数组,请考虑做一些数学运算!参见at()方法:

template<typename T > class array2d {

protected:
    std::vector< T > _dataStore;
    size_t _sx;

public:
    array2d(size_t sx, size_t sy = 1): _sx(sx), _dataStore(sx*sy) {}
    T& at( size_t x, size_t y ) { return _dataStore[ x+y*sx]; }
    const T& at( size_t x, size_t y ) const { return _dataStore[ x+y*sx]; }
    const T& get( size_t x, size_t y ) const { return at(x,y); }
    void set( size_t x, size_t y, const T& newValue ) { at(x,y) = newValue; }
};

0

使用C ++ 11和标准库,您可以在一行代码中创建一个非常漂亮的二维数组:

std::array<std::array<int, columnCount>, rowCount> myMatrix {0};

std::array<std::array<std::string, columnCount>, rowCount> myStringMatrix;

std::array<std::array<Widget, columnCount>, rowCount> myWidgetMatrix;

通过确定内部矩阵表示行,可以使用以下myMatrix[y][x]语法访问矩阵:

myMatrix[0][0] = 1;
myMatrix[0][3] = 2;
myMatrix[3][4] = 3;

std::cout << myMatrix[3][4]; // outputs 3

myStringMatrix[2][4] = "foo";
myWidgetMatrix[1][5].doTheStuff();

您可以将range- for用于输出:

for (const auto &row : myMatrix) {
  for (const auto &elem : row) {
    std::cout << elem << " ";
  }
  std::cout << std::endl;
}

(确定“内部” array表示列将允许使用一种foo[x][y]语法,但是您需要使用笨拙的for(;;)循环来显示输出。)


0

我的5美分。

我凭直觉知道我需要做很多样板代码。

这就是为什么我没有重载运算符[],而是重载了运算符(int,int)。然后在最终结果中,我做了m(1,2),而不是m [1] [2]

我知道这是不同的事情,但仍然非常直观,看起来像数学脚本。


0

最短,最简单的解决方案:

class Matrix
{
public:
  float m_matrix[4][4];

// for statements like matrix[0][0] = 1;
  float* operator [] (int index) 
  {
    return m_matrix[index];
  }

// for statements like matrix[0][0] = otherMatrix[0][0];
  const float* operator [] (int index) const 
  {
    return m_matrix[index];
  }

};
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.