संचालक [] [] अधिभार


93

क्या ओवरलोड करना संभव है [] ऑपरेटर को दो बार ? अनुमति देने के लिए, कुछ इस तरह: function[3][3](दो आयामी सरणी में)।

यदि यह संभव है, तो मैं कुछ उदाहरण कोड देखना चाहूंगा।


23
Btw, यह बहुत आसान है और अधिक सामान्य operator()(int, int)इसके बजाय अधिभार है ...
उलटा

2
पहिया फिर से क्यों? बस std::vectorएक रेंज कंस्ट्रक्टर के साथ उपयोग करें : stackoverflow.com/a/25405865/610351
जियोफॉयर

या आप बस कुछ का उपयोग कर सकते हैं जैसे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
रयान हेनिंग

1
std::vector<std::vector<int>>कॉपी पर मेमलेक और अजीब व्यवहार से बचने के लिए उपयोग करने के लिए बेहतर है ।
Jarod42

बूस्ट दोनों 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[]इस कोड में एक संदर्भ नहीं लौटता है (यह मानते हुए कि आपकी टिप्पणी रयान हेनिंग के जवाब में नहीं है)। इससे भी महत्वपूर्ण बात arrयह है कि अगर कांस्टेबल है तो operator[]इसका इस्तेमाल नहीं किया जा सकता है। आपको एक कास्ट संस्करण को परिभाषित करना होगा, और निश्चित रूप से आप इसे वापस कर देंगे const Proxy। तब Proxyअपने आप में कॉन्स्टेबल और नॉन-कॉस्ट तरीके होते। और फिर आपका उदाहरण अभी भी संकलित नहीं होगा, और प्रोग्रामर खुश होगा कि ब्रह्मांड में सब कुछ ठीक है और अच्छा है। =)
धान

21

एक अभिव्यक्ति के लिए x[y][z]आवश्यक है कि वह उस x[y]वस्तु का मूल्यांकन करे dजो समर्थन करती है d[z]

इसका मतलब यह है कि x[y]एक वस्तु के साथ होना चाहिए जो operator[]एक "प्रॉक्सी ऑब्जेक्ट" का मूल्यांकन करता है जो एक का भी समर्थन करता है operator[]

उन्हें श्रृंखलाबद्ध करने का यही एकमात्र तरीका है।

वैकल्पिक रूप से, operator()कई तर्कों को लेने के लिए अधिभार , जैसे कि आप आह्वान कर सकते हैंmyObject(x,y)


कोष्ठक का अधिभार दो इनपुट प्राप्त करने की अनुमति क्यों देता है लेकिन आप कोष्ठक के साथ ऐसा नहीं कर सकते हैं?
ए। उन्माद

20

दो आयामी सरणी के लिए, विशेष रूप से, आप एक एकल ऑपरेटर [] अधिभार के साथ दूर हो सकते हैं जो प्रत्येक पंक्ति के पहले तत्व के लिए एक संकेतक लौटाता है।

फिर आप पंक्ति में प्रत्येक तत्व तक पहुंचने के लिए अंतर्निहित इंडेक्सिंग ऑपरेटर का उपयोग कर सकते हैं।


4
मुझे सबसे व्यावहारिक और कुशल समाधान पसंद है। आश्चर्य है कि इसे अधिक वोट क्यों नहीं मिले - शायद इसलिए कि इसमें आंख को पकड़ने वाला कोड नहीं है।
यिगल रीस

16

यह संभव है यदि आप पहले [] कॉल में किसी प्रकार की प्रॉक्सी क्लास लौटाते हैं। हालांकि, एक अन्य विकल्प है: आप ऑपरेटर () को ओवरलोड कर सकते हैं जो किसी भी तर्क को स्वीकार कर सकते हैं ( function(3,3))।


9

एक दृष्टिकोण का उपयोग कर रहा है 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];
}

बेशक, आप कर सकते typedefहैंpair<int,int>


8
यह C ++ 11 और ब्रेस इनिशियलाइज़ेशन के साथ बहुत अधिक आकर्षक हो जाता है । अब आप लिख सकते हैंnValue = theArray[{2,3}];
मार्टिन बोनेर

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()ऐसा विकल्प है जो दो तर्कों के रूप में ओनेक्स पर दोनों निर्देशांक को पारित करता है। अब [][]समर्थन लिखना सिर्फ है:

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

के साथ 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])));
}

जीवंत उदाहरण

इस समाधान में आपको वास्तविक एसटीएल कंटेनर प्रदान करने का लाभ है, इसलिए आप लूप, एसटीएल एल्गोरिदम और इतने पर विशेष उपयोग कर सकते हैं।

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

वेक्टर <वेक्टर <टी>> या टी ** की आवश्यकता केवल तभी होती है जब आपके पास चर लंबाई की पंक्तियाँ होती हैं और स्मृति उपयोग / आवंटन के मामले में बहुत अक्षम होती हैं यदि आपको आयताकार सरणी की आवश्यकता होती है तो इसके बजाय कुछ गणित करने पर विचार करें! देखें () विधि:

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 और मानक लाइब्रेरी का उपयोग करके आप कोड की एक पंक्ति में एक बहुत अच्छा दो-आयामी सरणी बना सकते हैं:

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

और आप उपयोग कर सकते हैं- forउत्पादन के लिए:

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

(भीतर के arrayकॉलम को तय करना एक foo[x][y]सिंटैक्स के लिए अनुमति देता है लेकिन आपको for(;;)आउटपुट प्रदर्शित करने के लिए अनाड़ी छोरों का उपयोग करना होगा ।)


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

};
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.