it-swarm.com.de

Operator [] [] Überladung

Kann der []-Operator zweimal überladen werden? Um etwas zu erlauben, etwa so: function[3][3] (wie in einem zweidimensionalen Array). 

Wenn es möglich ist, würde ich gerne Beispielcode sehen.

80
icepopo

Sie können operator[] überladen, um ein Objekt zurückzugeben, für das Sie operator[] erneut verwenden können, um ein Ergebnis zu erhalten.

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

Dann können Sie es wie folgt verwenden:

ArrayOfArrays aoa;
aoa[3][5];

Dies ist nur ein einfaches Beispiel. Sie möchten eine Reihe von Grenzüberprüfungen hinzufügen, aber Sie haben die Idee.

104
Seth Carnegie

Ein Ausdruck x[y][z] erfordert, dass x[y] ein Objekt d auswertet, das d[z] unterstützt.

Dies bedeutet, dass x[y] ein Objekt mit einem operator[] sein sollte, das ein "Proxy-Objekt" auswertet, das also einen operator[] unterstützt.

Nur so können sie verkettet werden.

Alternativ können Sie operator() überladen, um mehrere Argumente zu übernehmen, sodass Sie myObject(x,y) aufrufen können.

Bei einem zweidimensionalen Array kann es vorkommen, dass Sie einen einzigen Operator [] überladen, der einen Zeiger auf das erste Element jeder Zeile zurückgibt. 

Dann können Sie den integrierten Indexierungsoperator verwenden, um auf jedes Element in der Zeile zuzugreifen.

17
Bo Persson

Es ist möglich, wenn Sie beim ersten [] Aufruf eine Art Proxy-Klasse zurückgeben. Es gibt jedoch eine andere Option: Sie können operator () überladen, der eine beliebige Anzahl von Argumenten (function(3,3)) akzeptiert.

16
John

Ein Ansatz verwendet 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];
}

Natürlich können Sie typedef den pair<int,int>

7
Ajay

Sie können ein Proxy-Objekt verwenden, etwa so:

#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
Node

Es wäre toll, wenn Sie mir mitteilen könnten, was function, function[x] und function[x][y] sind. Aber lassen Sie mich es trotzdem als ein Objekt betrachten, das irgendwo wie deklariert wurde

SomeClass function;

(Da Sie sagten, dass es sich um eine Überladung des Operators handelt, werden Sie wahrscheinlich nicht an Arrays wie SomeClass function[16][32]; interessiert sein.)

function ist also eine Instanz vom Typ SomeClass. Suchen Sie dann nach der Deklaration von SomeClass für den Rückgabetyp von operator[] overload, genau wie

ReturnType operator[](ParamType);

Dann hat function[x] den Typ ReturnType. Suchen Sie erneut nach ReturnType für die operator[] -Überladung. Wenn es eine solche Methode gibt, können Sie den Ausdruck function[x][y] verwenden.

Beachten Sie, dass function[x][y] im Gegensatz zu function(x, y) zwei separate Aufrufe sind. Es ist also schwer für den Compiler oder die Laufzeit, die Atomizität zu gewährleisten, es sei denn, Sie verwenden eine Sperre im Kontext. Ein ähnliches Beispiel ist, dass libc sagt, dass printf atomar ist, während sukzessive Aufrufe des überlasteten operator<< im Ausgabestream nicht sind. Eine Aussage wie

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

möglicherweise liegt ein Problem in einer Multithread-Anwendung vor, aber so etwas wie

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

ist gut.

4
neuront
#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
Kaustav Ray
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)};}

Dadurch können Sie ein Lambda nehmen und einen Indexer erstellen (mit []-Unterstützung).

Angenommen, Sie haben eine operator(), die die Übergabe beider Koordinaten bei onxe als zwei Argumente unterstützt. Nun schreiben Sie [][]-Unterstützung:

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

Und fertig. Keine benutzerdefinierte Klasse erforderlich.

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

Ich habe meine eigene einfache Lösung gefunden.

2
Grandstack

Es ist möglich, mehrere [] mit einem speziellen Vorlagenhandler zu überladen. Nur um zu zeigen, wie es funktioniert: 

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

Und nun die Definition von SubscriptHandler<ClassType,ArgType,RetType,N>, damit der vorherige Code funktioniert. Es zeigt nur, wie es geht. Diese Lösung ist weder optimal noch fehlerfrei (z. B. nicht threadsicher).

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

Wenn Sie anstelle von [x] [y] a [{x, y}] sagen möchten, können Sie Folgendes tun:

struct Coordinate {  int x, y; }

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

Beispielcode:

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
Anant Rai

der Vektor <Vektor <T>> oder T ** wird nur benötigt, wenn Sie Zeilen mit variabler Länge haben und viel zu ineffizient in Bezug auf die Speicherauslastung/-zuordnung Wenn Sie ein rechteckiges Array benötigen, ziehen Sie stattdessen etwas Mathematik in Betracht! siehe at () -Methode:

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
xakepp35

Die kürzeste und einfachste Lösung:

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

};
0
Vegeta

Meine 5 Cent.

Ich wusste intuitiv, dass ich viel Boilerplate-Code machen muss.

Deshalb habe ich statt des Operators [] den Operator (int, int) überladen. Dann im Endergebnis, anstelle von m [1] [2], habe ich m (1,2)

Ich weiß, es ist eine andere Sache, aber immer noch sehr intuitiv und sieht aus wie ein mathematisches Skript.

0
Nick

Mit C++ 11 und der Standard Library können Sie ein sehr schönes zweidimensionales Array in einer einzigen Codezeile erstellen:

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;

Wenn Sie entscheiden, dass die innere Matrix Zeilen darstellt, greifen Sie mit einer myMatrix[y][x]-Syntax auf die Matrix zu:

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

Und Sie können ranged -for für die Ausgabe verwenden:

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

(Das Festlegen der inneren array-Spalte würde eine foo[x][y]-Syntax zulassen, aber Sie müssten zur Anzeige der Ausgabe unbeholfenere for(;;)-Schleifen verwenden.

0
Jack Deeth

Mit einem std::vector<std::vector<type*>> können Sie den inneren Vektor mit einem benutzerdefinierten Eingabeoperator erstellen, der Ihre Daten durchläuft und einen Zeiger auf alle Daten zurückgibt.

Zum Beispiel:

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

Live-Beispiel

Diese Lösung bietet den Vorteil, dass Sie einen echten STL-Container erhalten, sodass Sie special für Schleifen, STL-Algorithmen usw. verwenden können.

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

Es werden jedoch Zeigervektoren erstellt. Wenn Sie also kleine Datenstrukturen wie diese verwenden, können Sie den Inhalt direkt in das Array kopieren.

0
Geoffroy