Operator [] [] เกินพิกัด


93

เป็นไปได้ไหมที่จะโอเวอร์โหลด[]ตัวดำเนินการสองครั้ง ในการอนุญาตสิ่งนี้: function[3][3](เช่นในอาร์เรย์สองมิติ)

ถ้าเป็นไปได้ฉันต้องการดูโค้ดตัวอย่าง


24
Btw มันง่ายกว่าและธรรมดากว่ามากที่จะโอเวอร์โหลดoperator()(int, int)แทน ...
ผกผัน

2
ทำไมต้องสร้างล้อใหม่? เพียงใช้std::vectorกับ range constructor: stackoverflow.com/a/25405865/610351
Geoffroy

หรือจะใช้อะไรก็ได้เช่นusing array2d = std::array<std::array<int, 3>, 3>;
adembudak

คำตอบ:


121

คุณสามารถโอเวอร์โหลด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
ไรอัน Haining

1
ควรใช้std::vector<std::vector<int>>เพื่อหลีกเลี่ยง memleak และพฤติกรรมแปลก ๆ ในการคัดลอก
จรด 42

ทั้ง Boost multi_arrayและextent_genเป็นตัวอย่างที่ดีของเทคนิคนี้ boost.org/doc/libs/1_57_0/libs/multi_array/doc/…
alfC

1
แต่const ArrayOfArrays arr; arr[3][5] = 42;จะสามารถที่จะผ่านการรวบรวมและการเปลี่ยนแปลงarr[3][5]ซึ่งเป็นอย่างใดแตกต่างจากสิ่งที่คาดหวังของผู้ใช้ที่เป็นarr const
abcdabcd987

5
@ abcdabcd987 ไม่ถูกต้องด้วยเหตุผลสองสามประการ ขั้นแรกProxy::operator[]อย่าส่งคืนข้อมูลอ้างอิงในรหัสนี้ (สมมติว่าความคิดเห็นของคุณไม่ได้ตอบกลับ Ryan Haining) ที่สำคัญถ้าarrเป็น const แล้วoperator[]จะไม่สามารถใช้งานได้ คุณจะต้องกำหนดเวอร์ชัน const และแน่นอนว่าคุณจะต้องคืนconst Proxyค่า จากนั้นProxyตัวมันเองจะมีวิธี const และ non-const จากนั้นตัวอย่างของคุณก็ยังไม่สามารถรวบรวมได้และโปรแกรมเมอร์ก็ยินดีที่ทุกอย่างเรียบร้อยและดีในจักรวาล =)
ข้าวเปลือก

21

การแสดงออกx[y][z]ต้องการให้x[y]ประเมินไปยังวัตถุที่สนับสนุนdd[z]

ซึ่งหมายความว่าx[y]ควรจะเป็นวัตถุที่มีoperator[]ประเมินว่าเป็น "วัตถุพร็อกซี่" ที่ยังoperator[]สนับสนุน

นี่เป็นวิธีเดียวที่จะล่ามโซ่พวกเขา

อีกทางเลือกหนึ่งเกินที่จะใช้ข้อโต้แย้งหลายอย่างเช่นที่คุณอาจเรียกoperator()myObject(x,y)


เหตุใดวงเล็บที่มากเกินไปจึงอนุญาตให้รับอินพุตสองอินพุต แต่คุณไม่สามารถทำสิ่งเดียวกันกับวงเล็บได้
อ. Frenzy

20

สำหรับอาร์เรย์สองมิติโดยเฉพาะคุณอาจหนีไปได้ด้วยตัวดำเนินการเดียว [] โอเวอร์โหลดที่ส่งกลับตัวชี้ไปยังองค์ประกอบแรกของแต่ละแถว

จากนั้นคุณสามารถใช้ตัวดำเนินการจัดทำดัชนีในตัวเพื่อเข้าถึงแต่ละองค์ประกอบภายในแถว


4
ดูเหมือนว่าฉันจะเป็นโซลูชันที่ใช้ได้จริงและมีประสิทธิภาพมากที่สุด สงสัยว่าทำไมถึงไม่ได้รับการโหวตมากกว่า - อาจเป็นเพราะไม่มีรหัสที่ดึงดูดสายตา
Yigal Reiss

16

เป็นไปได้ถ้าคุณส่งคืนคลาสพร็อกซีบางประเภทในการโทร [] ครั้งแรก อย่างไรก็ตามมีตัวเลือกอื่น: คุณสามารถโอเวอร์โหลดตัวดำเนินการ () ที่สามารถรับอาร์กิวเมนต์จำนวนเท่าใดก็ได้ ( function(3,3))


10

แนวทางหนึ่งคือการใช้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>


9
นี้จะกลายเป็นมากน่าสนใจมากขึ้นด้วย C ++ เริ่มต้นที่ 11 และรั้ง ตอนนี้คุณสามารถเขียนnValue = theArray[{2,3}];
Martin Bonner สนับสนุน Monica

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

มันจะดีหากคุณสามารถให้เราทราบว่าfunction, function[x]และfunction[x][y]มี แต่อย่างไรก็ตามขอผมพิจารณาว่ามันเป็นวัตถุที่ประกาศที่ไหนสักแห่งเช่น

SomeClass function;

(เพราะคุณบอกว่ามันเกินตัวดำเนินการฉันคิดว่าคุณจะไม่สนใจอาร์เรย์SomeClass function[16][32];)

ดังนั้นเป็นตัวอย่างของประเภทfunction SomeClassจากนั้นค้นหาคำประกาศSomeClassสำหรับประเภทการส่งคืนของการ operator[]โอเวอร์โหลดเช่นเดียวกับ

ReturnType operator[](ParamType);

แล้วfunction[x]จะมีประเภทReturnType. มองหาReturnTypeการoperator[]โอเวอร์โหลดอีกครั้ง หากมีวิธีการดังกล่าวคุณสามารถใช้นิพจน์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)};}

วิธีนี้ช่วยให้คุณใช้แลมด้าและสร้างดัชนี (พร้อม[]การสนับสนุน)

สมมติว่าคุณมี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);}
  );
}

และเสร็จแล้ว ไม่จำเป็นต้องมีคลาสที่กำหนดเอง


2

ถ้าแทนที่จะพูดว่า [x] [y] คุณต้องการพูดว่า [{x, y}] คุณสามารถทำได้ดังนี้:

struct Coordinate {  int x, y; }

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

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

ด้วย a 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 จริงให้คุณดังนั้นคุณจึงสามารถใช้แบบพิเศษสำหรับลูปอัลกอริทึม 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 ** ก็ต่อเมื่อคุณมีแถวที่มีความยาวตัวแปรและไม่มีประสิทธิภาพมากเกินไปในแง่ของการใช้ / การจัดสรรหน่วยความจำหากคุณต้องการอาร์เรย์สี่เหลี่ยมให้ลองคำนวณทางคณิตศาสตร์แทน! ดูที่ () วิธีการ:

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 และ Standard Library คุณสามารถสร้างอาร์เรย์สองมิติที่ดีมากในโค้ดบรรทัดเดียว:

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

และคุณสามารถใช้ ranged- forสำหรับเอาต์พุต:

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

(การตัดสินใจว่าarrayคอลัมน์แสดงด้านในจะอนุญาตให้ใช้foo[x][y]ไวยากรณ์ได้ แต่คุณต้องใช้for(;;)ลูปที่clumsier เพื่อแสดงผลลัพธ์)


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

};
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.