Operator [] [] kelebihan beban

93

Apakah mungkin membebani []operator dua kali? Untuk mengizinkan, sesuatu seperti ini: function[3][3](seperti dalam array dua dimensi).

Jika memungkinkan, saya ingin melihat beberapa contoh kode.

icepopo.dll
sumber
24
Ngomong-ngomong, itu jauh lebih sederhana dan lebih umum untuk membebani operator()(int, int)...
Terbalik
2
Mengapa mengolah kembali roda? Cukup gunakan std::vectordengan konstruktor rentang: stackoverflow.com/a/25405865/610351
Geoffroy
Atau Anda bisa menggunakan sesuatu sepertiusing array2d = std::array<std::array<int, 3>, 3>;
adembudak

Jawaban:

121

Anda bisa membebani operator[]untuk mengembalikan objek yang bisa Anda gunakan operator[]lagi untuk mendapatkan hasil.

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

Kemudian Anda bisa menggunakannya seperti:

ArrayOfArrays aoa;
aoa[3][5];

Ini hanyalah contoh sederhana, Anda ingin menambahkan banyak pemeriksaan batas dan semacamnya, tetapi Anda mengerti.

Seth Carnegie
sumber
5
bisa menggunakan destruktor. Dan Proxy::operator[]harus kembali int&tidak hanyaint
Ryan Haining
1
Lebih baik digunakan std::vector<std::vector<int>>untuk menghindari perilaku memleak dan aneh pada salinan.
Jarod42
Baik Boost multi_arraydan extent_genmerupakan contoh bagus dari teknik ini. boost.org/doc/libs/1_57_0/libs/multi_array/doc/…
alfC
1
Namun, const ArrayOfArrays arr; arr[3][5] = 42;akan mampu melewati kompilasi dan perubahan arr[3][5], yang entah bagaimana berbeda dari apa harapan pengguna yang arradalah const.
abcdabcd987
5
@ abcdabcd987 Itu tidak benar karena beberapa alasan. Pertama, Proxy::operator[]tidak mengembalikan referensi dalam kode ini (dengan asumsi komentar Anda tidak membalas Ryan Haining). Lebih penting lagi, jika arradalah const maka operator[]tidak dapat digunakan. Anda harus mendefinisikan versi const, dan tentu saja Anda akan membuatnya kembali const Proxy. Maka Proxyitu sendiri akan memiliki metode const dan non-const. Dan kemudian contoh Anda masih belum dapat dikompilasi, dan programmer akan senang bahwa semuanya baik-baik saja di alam semesta. =)
paddy
21

Ekspresi x[y][z]mengharuskan x[y]mengevaluasi objek dyang mendukung d[z].

Ini berarti bahwa x[y]harus menjadi objek dengan operator[]yang mengevaluasi ke "objek proxy" yang juga mendukung operator[].

Ini adalah satu-satunya cara untuk merantai mereka.

Atau, lakukan overload operator()untuk mengambil beberapa argumen, seperti yang mungkin Anda panggil myObject(x,y).

Balapan Ringan dalam Orbit
sumber
Mengapa kelebihan tanda kurung memungkinkan mendapatkan dua masukan tetapi Anda tidak dapat melakukan hal yang sama dengan tanda kurung?
A. Frenzy
20

Untuk larik dua dimensi, khususnya, Anda mungkin lolos dengan kelebihan beban operator [] yang mengembalikan penunjuk ke elemen pertama dari setiap baris.

Kemudian Anda dapat menggunakan operator pengindeksan bawaan untuk mengakses setiap elemen dalam baris.

Bo Persson
sumber
4
Menurut saya solusi yang paling praktis dan efisien. Bertanya-tanya mengapa tidak mendapatkan lebih banyak suara - mungkin karena tidak memiliki kode yang menarik.
Yigal Reiss
16

Hal ini dimungkinkan jika Anda mengembalikan beberapa jenis kelas proxy dalam panggilan [] pertama. Namun, ada opsi lain: Anda bisa membebani operator () yang bisa menerima sejumlah argumen ( function(3,3)).

John
sumber
10

Salah satu pendekatannya menggunakan 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];
}

Tentu saja, Anda mungkin typedefsajapair<int,int>

Ajay
sumber
9
Ini menjadi jauh lebih menarik dengan C ++ 11 dan inisialisasi brace. Sekarang Anda dapat menulisnValue = theArray[{2,3}];
Martin Bonner mendukung Monica
5

Anda dapat menggunakan objek proxy, seperti ini:

#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;
}
Node
sumber
4

Akan sangat bagus jika Anda bisa memberi tahu saya apa function, function[x]dan apa function[x][y]adanya. Tapi bagaimanapun izinkan saya menganggapnya sebagai objek yang dideklarasikan di suatu tempat

SomeClass function;

(Karena Anda mengatakan bahwa operator itu kelebihan beban, saya pikir Anda tidak akan tertarik pada array seperti SomeClass function[16][32];)

Jadi functionadalah contoh tipe SomeClass. Kemudian cari deklarasi SomeClassuntuk jenis operator[]kelebihan muatan yang dikembalikan , seperti

ReturnType operator[](ParamType);

Kemudian function[x]akan memiliki tipe ReturnType. Lagi mencari ReturnTypeuntuk operator[]overload. Jika ada metode seperti itu, Anda dapat menggunakan ekspresi function[x][y].

Catatan, tidak seperti function(x, y), function[x][y]ada 2 panggilan terpisah. Jadi, sulit bagi kompiler atau runtime untuk menjamin atomicity kecuali Anda menggunakan kunci dalam konteksnya. Contoh serupa adalah, libc mengatakan printfadalah atom sementara panggilan berturut-turut ke kelebihan beban operator<<dalam aliran keluaran tidak. Pernyataan seperti

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

mungkin ada masalah dalam aplikasi multi-utas, tetapi sesuatu seperti

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

baik-baik saja.

neuront
sumber
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;
}
Kaustav Ray
sumber
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];
};

Menemukan solusi sederhana saya sendiri untuk ini.

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

Ini memungkinkan Anda mengambil lambda, dan menghasilkan pengindeks (dengan []dukungan).

Misalkan Anda memiliki operator()yang mendukung penerusan kedua koordinat di onxe sebagai dua argumen. Sekarang [][]dukungan menulis hanyalah:

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

Dan selesai. Tidak ada kelas khusus yang diperlukan.

Yakk - Adam Nevraumont
sumber
2

Jika, alih-alih mengucapkan [x] [y], Anda ingin mengucapkan [{x, y}], Anda dapat melakukan seperti ini:

struct Coordinate {  int x, y; }

class Matrix {
    int** data;
    operator[](Coordinate c) {
        return data[c.y][c.x];
    }
}
Erel Segal-Halevi
sumber
1

Dimungkinkan untuk membebani lebih dari satu [] menggunakan penangan template khusus. Hanya untuk menunjukkan cara kerjanya:

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

Dan sekarang definisi SubscriptHandler<ClassType,ArgType,RetType,N>untuk membuat kode sebelumnya berfungsi. Itu hanya menunjukkan bagaimana itu bisa dilakukan. Solusi ini optimal atau bebas bug (tidak threadsafe misalnya).

#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);
    }
};
Frédéric Terrazzoni
sumber
0

Dengan a std::vector<std::vector<type*>>, Anda dapat membuat vektor bagian dalam menggunakan operator input khusus yang mengulangi data Anda dan mengembalikan pointer ke setiap data.

Sebagai contoh:

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

Contoh langsung

Solusi ini memiliki keuntungan karena memberi Anda wadah STL nyata, sehingga Anda dapat menggunakan khusus untuk loop, algoritma STL, dan sebagainya.

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

Namun, itu membuat vektor pointer, jadi jika Anda menggunakan struktur data kecil seperti ini, Anda dapat langsung menyalin konten di dalam larik.

Geoffroy
sumber
0

Kode sampel:

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;
}
Anant Rai
sumber
0

vektor <vektor <T>> atau T ** diperlukan hanya jika Anda memiliki baris dengan panjang variabel dan terlalu tidak efisien dalam hal alokasi / penggunaan memori jika Anda memerlukan larik persegi panjang, pertimbangkan untuk melakukan matematika sebagai gantinya! lihat di metode ():

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; }
};
xakepp35
sumber
0

Menggunakan C ++ 11 dan Perpustakaan Standar Anda dapat membuat larik dua dimensi yang sangat bagus dalam satu baris kode:

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;

Dengan memutuskan matriks dalam mewakili baris, Anda mengakses matriks dengan myMatrix[y][x]sintaks:

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

Dan Anda dapat menggunakan ranged- foruntuk keluaran:

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

(Memutuskan arraykolom mewakili bagian dalam akan memungkinkan untuk foo[x][y]sintaks tetapi Anda harus menggunakan for(;;)loop yang lebih kaku untuk menampilkan output.)

Jack Deeth
sumber
0

5 sen saya.

Saya secara intuitif tahu saya perlu melakukan banyak kode boilerplate.

Inilah sebabnya, alih-alih operator [], saya melakukan operator yang kelebihan beban (int, int). Kemudian di hasil akhir, alih-alih m [1] [2], saya lakukan m (1,2)

Saya tahu ini adalah hal yang BERBEDA, tetapi masih sangat intuitif dan terlihat seperti skrip matematika.

Nick
sumber
0

Solusi terpendek dan termudah:

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

};
Vegeta
sumber