Toán tử [] [] quá tải


90

Có thể quá tải []nhà điều hành hai lần? Để cho phép, một cái gì đó như thế này: function[3][3](giống như trong một mảng hai chiều).

Nếu có thể, tôi muốn xem một số mã ví dụ.


22
Btw, nó đơn giản hơn nhiều và phổ biến hơn để quá tải operator()(int, int)thay vì ...
Inverse

2
Tại sao phải tạo lại bánh xe? Chỉ cần sử dụng std::vectorvới một constructor khoảng: stackoverflow.com/a/25405865/610351
Geoffroy

Hoặc bạn có thể chỉ cần sử dụng một cái gì đó nhưusing array2d = std::array<std::array<int, 3>, 3>;
adem

Câu trả lời:


118

Bạn có thể nạp chồng operator[]để trả về một đối tượng mà bạn có thể sử dụng operator[]lại để lấy kết quả.

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

Sau đó, bạn có thể sử dụng nó như:

ArrayOfArrays aoa;
aoa[3][5];

Đây chỉ là một ví dụ đơn giản, bạn sẽ muốn thêm một loạt các giới hạn kiểm tra và nhiều thứ, nhưng bạn có ý tưởng.


5
có thể sử dụng một trình hủy. Và Proxy::operator[]nên trở về int&không chỉint
Ryan Haining

1
Tốt hơn nên sử dụng std::vector<std::vector<int>>để tránh ghi nhớ và hành vi lạ khi sao chép.
Jarod42

Cả hai Boost của multi_arrayextent_genlà những ví dụ tốt về kỹ thuật này. boost.org/doc/libs/1_57_0/libs/multi_array/doc/…
alfC

1
Tuy nhiên, const ArrayOfArrays arr; arr[3][5] = 42;sẽ có thể vượt qua biên soạn và những thay đổi arr[3][5], đó là bằng cách nào đó khác với những gì mong đợi của người sử dụng mà arrconst.
abcdabcd987

5
@ abcdabcd987 Điều đó không chính xác vì một vài lý do. Đầu tiên, Proxy::operator[]không trả lại tham chiếu trong mã này (giả sử nhận xét của bạn không trả lời Ryan Haining). Quan trọng hơn, nếu arrlà const thì operator[]không thể được sử dụng. Bạn sẽ phải xác định một phiên bản const, và tất nhiên bạn sẽ làm cho nó trả về const Proxy. Sau đó, Proxychính nó sẽ có các phương thức const và không phải const. Và sau đó ví dụ của bạn sẽ vẫn không được biên dịch, và lập trình viên sẽ rất vui vì tất cả đều ổn và tốt trong vũ trụ. =)
paddy

21

Một biểu thức x[y][z]yêu cầu x[y]đánh giá một đối tượng dhỗ trợ d[z].

Điều này có nghĩa rằng đó x[y]phải là một đối tượng với một operator[]đánh giá là một "đối tượng proxy" cũng hỗ trợ một operator[].

Đây là cách duy nhất để xâu chuỗi chúng.

Ngoài ra, quá tải operator()để nhận nhiều đối số, như vậy bạn có thể gọi myObject(x,y).


Tại sao quá tải của dấu ngoặc cho phép nhận hai đầu vào nhưng bạn không thể làm tương tự với dấu ngoặc?
A. Frenzy

19

Đặc biệt, đối với mảng hai chiều, bạn có thể sử dụng một toán tử đơn lẻ [] quá tải trả về một con trỏ đến phần tử đầu tiên của mỗi hàng.

Sau đó, bạn có thể sử dụng toán tử lập chỉ mục tích hợp để truy cập từng phần tử trong hàng.


4
Với tôi, đó là giải pháp thiết thực và hiệu quả nhất. Tự hỏi tại sao nó không nhận được nhiều phiếu bầu hơn - có thể vì nó không có mã bắt mắt.
Yigal Reiss

16

Có thể xảy ra nếu bạn trả về một số loại lớp proxy trong lần gọi [] đầu tiên. Tuy nhiên, có một tùy chọn khác: bạn có thể nạp chồng toán tử () có thể chấp nhận bất kỳ số lượng đối số nào ( function(3,3)).


9

Một cách tiếp cận đang sử dụng 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];
}

Tất nhiên, bạn có thể typedefcácpair<int,int>


8
Điều này trở nên hấp dẫn hơn rất nhiều với C ++ 11 và khởi tạo dấu ngoặc nhọn. Bây giờ bạn có thể viếtnValue = theArray[{2,3}];
Martin Bonner hỗ trợ Monica

5

Bạn có thể sử dụng một đối tượng proxy, giống như sau:

#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

Nó sẽ là tuyệt vời nếu bạn có thể cho tôi biết những gì function, function[x]function[x][y]đang có. Nhưng dù sao, hãy để tôi coi nó như một đối tượng được khai báo ở đâu đó như

SomeClass function;

(Vì bạn đã nói rằng đó là quá tải toán tử, tôi nghĩ bạn sẽ không quan tâm đến mảng như thế này SomeClass function[16][32];)

Vì vậy, functionlà một thể hiện của loại SomeClass. Sau đó, tra cứu khai báo SomeClasscho kiểu operator[]quá tải trả về , giống như

ReturnType operator[](ParamType);

Sau đó, function[x]sẽ có các loại ReturnType. Một lần nữa tìm kiếm ReturnTypesự operator[]quá tải. Nếu có một phương thức như vậy, bạn có thể sử dụng biểu thức function[x][y].

Lưu ý, không giống như function(x, y), function[x][y]là 2 cuộc gọi riêng biệt. Vì vậy, thật khó cho trình biên dịch hoặc thời gian chạy đảm bảo tính nguyên tử trừ khi bạn sử dụng khóa trong ngữ cảnh. Một ví dụ tương tự là, libc nói printflà nguyên tử trong khi các lệnh gọi liên tiếp đến quá tải operator<<trong luồng đầu ra thì không. Một tuyên bố như

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

có thể gặp sự cố trong ứng dụng đa luồng, nhưng giống như

printf("%s%s", "hello", "\n");

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

Tìm thấy giải pháp đơn giản của riêng tôi cho điều này.


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

Điều này cho phép bạn lấy lambda và tạo ra một chỉ mục (có []hỗ trợ).

Giả sử bạn có một operator()hỗ trợ chuyển cả hai tọa độ tại onxe dưới dạng hai đối số. Bây giờ [][]hỗ trợ viết chỉ là:

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

Và thực hiện. Không yêu cầu lớp tùy chỉnh.


2

Nếu, thay vì nói [x] [y], bạn muốn nói [{x, y}], bạn có thể làm như sau:

struct Coordinate {  int x, y; }

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

1

Có thể nạp chồng nhiều [] bằng trình xử lý mẫu chuyên dụng. Chỉ để hiển thị cách nó hoạt động:

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

Và bây giờ là định nghĩa SubscriptHandler<ClassType,ArgType,RetType,N>để làm cho mã trước đó hoạt động. Nó chỉ cho thấy nó có thể được thực hiện như thế nào. Giải pháp này là tối ưu và không có lỗi (chẳng hạn như không phải threadsafe).

#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

Với a std::vector<std::vector<type*>>, bạn có thể xây dựng vectơ bên trong bằng cách sử dụng toán tử đầu vào tùy chỉnh để lặp qua dữ liệu của bạn và trả về một con trỏ cho mỗi dữ liệu.

Ví dụ:

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

Ví dụ trực tiếp

Giải pháp này có ưu điểm là cung cấp cho bạn một vùng chứa STL thực, vì vậy bạn có thể sử dụng các vòng lặp đặc biệt cho các vòng lặp, thuật toán STL, v.v.

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

Tuy nhiên, nó tạo vectơ con trỏ, vì vậy nếu bạn đang sử dụng cấu trúc dữ liệu nhỏ như cấu trúc này, bạn có thể sao chép trực tiếp nội dung bên trong mảng.


0

Mã mẫu:

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>> hoặc T ** chỉ được yêu cầu khi bạn có các hàng có độ dài thay đổi và cách quá kém hiệu quả về việc sử dụng / cấp phát bộ nhớ nếu bạn yêu cầu mảng chữ nhật, hãy cân nhắc thực hiện một số phép toán thay thế! xem tại phương thức ():

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

Sử dụng C ++ 11 và Thư viện chuẩn, bạn có thể tạo một mảng hai chiều rất đẹp trong một dòng mã:

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;

Bằng cách quyết định ma trận bên trong đại diện cho các hàng, bạn truy cập ma trận bằng myMatrix[y][x]cú pháp:

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

Và bạn có thể sử dụng ranged- forcho đầu ra:

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

(Quyết định các arraycột đại diện bên trong sẽ cho phép một foo[x][y]cú pháp nhưng bạn cần sử dụng for(;;)các vòng lặp vụng về hơn để hiển thị đầu ra.)


0

5 xu của tôi.

Tôi trực giác biết rằng tôi cần phải làm rất nhiều mã soạn sẵn.

Đây là lý do tại sao, thay vì toán tử [], tôi đã nạp chồng toán tử (int, int). Sau đó, trong kết quả cuối cùng, thay vì m [1] [2], tôi đã làm m (1,2)

Tôi biết đó là điều KHÁC BIỆT, nhưng vẫn rất trực quan và trông giống như tập lệnh toán học.


0

Giải pháp ngắn nhất và dễ dàng nhất:

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

};
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.