Operatör [] [] aşırı yükleme


93

[]Operatörü iki kez aşırı yüklemek mümkün mü ? İzin vermek için, bunun gibi bir şey: function[3][3](iki boyutlu bir dizide olduğu gibi).

Mümkünse, bazı örnek kodlar görmek isterim.


24
Btw, aşırı çok daha basit ve daha yaygın operator()(int, int)... yerine
Ters

2
Neden tekerleği yeniden yaratmalı? Yalnızca std::vectorbir aralık oluşturucuyla kullanın : stackoverflow.com/a/25405865/610351
Geoffroy

Ya da şöyle bir şey kullanabilirsinizusing array2d = std::array<std::array<int, 3>, 3>;
adembudak

Yanıtlar:


121

Sonuç almak operator[]için operator[]tekrar kullanabileceğiniz bir nesneyi döndürmek için aşırı yükleyebilirsiniz .

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

O zaman şu şekilde kullanabilirsiniz:

ArrayOfArrays aoa;
aoa[3][5];

Bu sadece basit bir örnek, bir sürü sınır kontrolü ve benzeri şeyler eklemek istersiniz, ancak fikri anladınız.


5
bir yıkıcı kullanabilir. Ve sadece Proxy::operator[]geri dönmemeliint&int
Ryan Haining

1
std::vector<std::vector<int>>Kopyalamada memleak ve garip davranışlardan kaçınmak için kullanmak daha iyidir.
Jarod42

Hem Boost en multi_arrayve extent_genbu tekniğin iyi örneklerdir. boost.org/doc/libs/1_57_0/libs/multi_array/doc/…
alfC

1
Ancak, const ArrayOfArrays arr; arr[3][5] = 42;derleme ve değişiklikleri geçmesi mümkün olacak arr[3][5]neler kullanıcıların beklenti bir şekilde farklı olduğu, arrbir const.
abcdabcd987

5
@ abcdabcd987 Birkaç nedenden dolayı bu doğru değil. İlk olarak, Proxy::operator[]bu kodda bir referans döndürmez (yorumunuzun Ryan Haining'e yanıt olmadığını varsayarak). Daha da önemlisi, arrconst ise operator[]kullanılamaz. Bir const sürümü tanımlamanız gerekir ve tabii ki geri dönmesini sağlarsınız const Proxy. O zaman Proxykendisi const ve const olmayan yöntemlere sahip olacaktır. Ve sonra örneğiniz hala derlenmez ve programcı evrende her şeyin yolunda ve güzel olmasına sevinir. =)
paddy

21

Bir ifade , destekleyen bir nesne olarak değerlendirilmesini x[y][z]gerektirir .x[y]dd[z]

Bu araçlar x[y]bir bir nesne olmalıdır operator[]"proxy nesnesi" bu değerlendirir da bir desteklemektedir operator[].

Onları zincirlemenin tek yolu bu.

Alternatif olarak, operator()birden çok argüman almak için aşırı yükleme yapın, öyle ki çağırabilirsiniz myObject(x,y).


Parantezlerin aşırı yüklenmesi neden iki girdi alınmasına izin veriyor, ancak aynısını parantezlerle yapamıyorsunuz?
A. Frenzy

20

İki boyutlu bir dizi için, özellikle, her satırın ilk öğesine bir işaretçi döndüren tek bir işleç [] aşırı yüklemesinden kurtulabilirsiniz.

Ardından, satırdaki her bir öğeye erişmek için yerleşik dizin oluşturma operatörünü kullanabilirsiniz.


4
Bana en pratik ve verimli çözüm gibi görünüyor. Neden daha fazla oy almadığını merak ediyorum - belki de göz alıcı bir kodu olmadığı içindir.
Yigal Reiss

16

İlk [] çağrısında bir tür proxy sınıfı döndürmeniz mümkündür. Ancak, başka bir seçenek daha vardır: herhangi bir sayıda argümanı ( function(3,3)) kabul edebilen işleci () aşırı yükleyebilirsiniz .


10

Bir yaklaşım std::pair<int,int>şunları kullanıyor :

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

Tabii ki, maytypedefpair<int,int>


9
Bu , C ++ 11 ve küme ayracı başlatma ile çok daha çekici hale geliyor . Şimdi yazabilirsiniznValue = theArray[{2,3}];
Martin Bonner Monica'yı

5

Bir proxy nesnesi kullanabilirsiniz, bunun gibi bir şey:

#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

Bana ne bildirin eğer Onun 'büyük olacak function, function[x]ve function[x][y]vardır. Ama yine de bunu bir yerde şu şekilde beyan edilmiş bir nesne olarak düşünmeme izin verin:

SomeClass function;

(Operatör aşırı yüklemesi olduğunu söylediğiniz için dizi gibi ilginizi çekmeyeceğini düşünüyorum SomeClass function[16][32];)

Yani functionbir tür örneği SomeClass. Daha sonra SomeClass, dönüş tipi operator[]aşırı yük beyanı için, aynı

ReturnType operator[](ParamType);

O function[x]zaman türüne sahip olacak ReturnType. Yine bakmak ReturnTypeiçin operator[]aşırı yük. Böyle bir yöntem varsa, ifadeyi kullanabilirsiniz function[x][y].

Not, aksine function(x, y), function[x][y]2 ayrı görüşmedir. Bu nedenle derleyici için zordur veya bağlamda bir kilit kullanmadığınız sürece çalışma zamanı atomikliği garanti eder. Benzer bir örnek, libc'nin printfatomik olduğunu söylerken operator<<, çıkış akışında art arda aşırı yüklenmiş çağrılara değil. Gibi bir ifade

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

çok iş parçacıklı uygulamada sorun olabilir, ancak

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

iyi.


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

Buna kendi basit çözümümü buldum.


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

Bu, bir lambda almanıza ve bir indeksleyici oluşturmanıza ( []destekli) izin verir.

operator()Her iki koordinatı onxe'de iki argüman olarak aktarmayı destekleyen bir hesabınız olduğunu varsayalım . Şimdi yazma [][]desteği sadece:

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

Ve bitti. Özel sınıf gerekmez.


2

Eğer, [x] [y] demek yerine, bir [{x, y}] demek isterseniz, şu şekilde yapabilirsiniz:

struct Coordinate {  int x, y; }

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

1

Özel bir şablon işleyici kullanarak birden çok [] 'a aşırı yükleme yapmak mümkündür. Nasıl çalıştığını göstermek için:

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

Ve şimdi SubscriptHandler<ClassType,ArgType,RetType,N>önceki kodu çalıştırmanın tanımı . Sadece nasıl yapılabileceğini gösterir. Bu çözüm optimaldir ve hatasızdır (örneğin iş parçacığı güvenli değildir).

#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

A ile std::vector<std::vector<type*>>, verilerinizi yineleyen ve her veriye bir işaretçi döndüren özel girdi operatörünü kullanarak iç vektörü oluşturabilirsiniz.

Örneğin:

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

Canlı örnek

Bu çözüm, size gerçek bir STL kapsayıcı sağlama avantajına sahiptir, böylece döngüler, STL algoritmaları vb. İçin özel kullanabilirsiniz.

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

Bununla birlikte, işaretçi vektörleri yaratır, bu nedenle, bunun gibi küçük veri yapıları kullanıyorsanız, içeriği dizinin içine doğrudan kopyalayabilirsiniz.


0

Basit kod:

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

vektör <vektör <T>> veya T ** yalnızca değişken uzunlukta satırlarınız olduğunda ve bellek kullanımı / ayırmaları açısından çok verimsiz olduğunda gereklidir, dikdörtgen diziye ihtiyacınız varsa bunun yerine biraz matematik yapmayı düşünün! () yöntemine bakın:

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'i ve Standart Kitaplığı kullanarak tek bir kod satırında çok güzel iki boyutlu bir dizi oluşturabilirsiniz:

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;

İç matrisin satırları temsil ettiğine karar vererek, matrise bir myMatrix[y][x]sözdizimi ile erişirsiniz :

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

Ve forçıktı için aralıklı kullanabilirsiniz :

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

(İç arraytemsil sütunlarına karar vermek bir foo[x][y]sözdizimine izin verir, ancak for(;;)çıktıyı görüntülemek için daha hantal döngüleri kullanmanız gerekir .)


0

5 sentim.

Çok sayıda standart kod yapmam gerektiğini sezgisel olarak biliyordum.

Bu nedenle, [] operatörü yerine operatörü (int, int) aşırı yükledim. Son sonuçta m [1] [2] yerine m (1,2) yaptım

FARKLI bir şey olduğunu biliyorum ama yine de çok sezgisel ve matematiksel bir senaryoya benziyor.


0

En kısa ve en kolay çözüm:

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

};
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.