Multidimensional array: operator overloading

I have a class with a multidimensional array:

  • can create one, two, ..., n dimensional arrays with this class

  • If the array has n dimensions, I want to use n operator[] to get the object:

example:

 A a({2,2,2,2}]; a[0][1][1][0] = 5; 

but the array is not a pointer vector, which leads to other vectors, etc.

so I want the [] operator to return a class object to the last dimension, then return an integer

This is a very simplified code, but it shows my problem:

The error I get is: "[Error] cannot convert 'A::B' to 'int' in initialization"

 #include <cstddef> // nullptr_t, ptrdiff_t, size_t #include <iostream> // cin, cout... class A { private: static int* a; public: static int dimensions; A(int i=0) { dimensions = i; a = new int[5]; for(int j=0; j<5; j++) a[j]=j; }; class B{ public: B operator[](std::ptrdiff_t); }; class C: public B{ public: int& operator[](std::ptrdiff_t); }; B operator[](std::ptrdiff_t); }; //int A::count = 0; A::BA::operator[] (std::ptrdiff_t i) { B res; if (dimensions <= 1){ res = C(); } else{ res = B(); } dimensions--; return res; } A::BA::B::operator[] (std::ptrdiff_t i){ B res; if (dimensions <=1){ res = B(); } else{ res = C(); } dimensions--; return res; } int& A::C::operator[](std::ptrdiff_t i){ return *(a+i); } int main(){ A* obj = new A(5); int res = obj[1][1][1][1][1]; std::cout<< res << std::endl; } 
+5
source share
3 answers

operator[] is evaluated from left to right in obj[1][1]...[1] , therefore obj[1] returns the object B Suppose you now have int res = obj[1] , then you assign an object B (or a C object in case of several calls [] ) int , but there is no conversion from B > or C to int . You probably need to write a conversion operator like

 operator int() { // convert to int here } 

for A , B and C , since overloaded operators are not inherited.

I got rid of your compilation error by simply writing such statements for A and B (of course, I have binding errors because there are undefined functions).

Also note that if you want to write something like obj[1][1]...[1] = 10 obj[1][1]...[1] = 10 , you need to overload operator= , since again there is no implicit conversion from int to A or your proxy objects.

Hope this makes sense.

PS: see also @Oncaphillis comment!

+3
source

vsoftco is absolutely right, you need to implement the overload operator if you want to actually access your elements. This is necessary if you want it to be dynamic as you describe it. I really thought this was an interesting problem, so I implemented what you described as a template. I think this works, but some things may be slightly disabled. Here is the code:

 template<typename T> class nDimArray { using thisT = nDimArray<T>; T m_value; std::vector<thisT*> m_children; public: nDimArray(std::vector<T> sizes) { assert(sizes.size() != 0); int thisSize = sizes[sizes.size() - 1]; sizes.pop_back(); m_children.resize(thisSize); if(sizes.size() == 0) { //initialize elements for(auto &c : m_children) { c = new nDimArray(T(0)); } } else { //initialize children for(auto &c : m_children) { c = new nDimArray(sizes); } } } ~nDimArray() { for(auto &c : m_children) { delete c; } } nDimArray<T> &operator[](const unsigned int index) { assert(!isElement()); assert(index < m_children.size()); return *m_children[index]; } //icky dynamic cast operators operator T() { assert(isElement()); return m_value; } T &operator=(T value) { assert(isElement()); m_value = value; return m_value; } private: nDimArray(T value) { m_value = value; } bool isElement() const { return m_children.size() == 0; } //no implementation yet nDimArray(const nDimArray&); nDimArray&operator=(const nDimArray&); }; 

The basic idea is that this class can either act as an array of arrays, or an element. This means that in fact an array of arrays MAY be an array of elements! When you want to get a value, it tries to pass it to the element, and if that doesn't work, it just throws an assertion error.

Hope this makes sense, and of course, if you have questions, I apologize! Actually, I hope you ask, because the scope of the problem you described is more than you probably think it is.

+2
source

To do this, it may be interesting to use the Russian doll style template class.

 // general template where 'd' indicates the number of dimensions of the container // and 'n' indicates the length of each dimension // with a bit more template magic, we could probably support each // dimension being able to have it own size template<size_t d, size_t n> class foo { private: foo<d-1, n> data[n]; public: foo<d-1, n>& operator[](std::ptrdiff_t x) { return data[x]; } }; // a specialization for one dimension. n can still specify the length template<size_t n> class foo<1, n> { private: int data[n]; public: int& operator[](std::ptrdiff_t x) { return data[x]; } }; int main(int argc, char** argv) { foo<3, 10> myFoo; for(int i=0; i<10; ++i) for(int j=0; j<10; ++j) for(int k=0; k<10; ++k) myFoo[i][j][k] = i*10000 + j*100 + k; return myFoo[9][9][9]; // would be 090909 in this case } 

Each dimension stores an array of elements from the previous dimension. Size 1 uses a core specialization that tracks the 1D int array. Then size 2 will contain an array of one-dimensional arrays, D3 will have an array of two-dimensional arrays, etc. Then access looks the same as native multidimensional arrays. I use arrays inside a class in my example. This makes all memory continuous for n-dimensional arrays and does not require dynamic allocations within the class. However, you can provide the same functionality with dynamic allocation.

+1
source

Source: https://habr.com/ru/post/1205910/


All Articles