Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Operator[] Overloading in MultiDimensional Arrays c++

When I call: a7[0][1][100];

I am able to obtain the first index 0 in the operator[] but as index I won't able to obtain other index values 1 and 100 as recursively. How could I able to use operator[] in order to obtain recursive following index values. In this example for the 3 dimensional array, operator[] is only called only once for the first dimension which is 0.

My Example Code is as follows:

template <class T, unsigned ... RestD> struct array;

template <class T, unsigned PrimaryD>
struct array <T, PrimaryD> {
    typedef T type[PrimaryD];

    type data;

    T& operator[] (unsigned i) {
        return data[i];
    }
}; 

template <class T, unsigned PrimaryD, unsigned ... RestD>
struct array <T, PrimaryD, RestD...> {
    typedef typename array<T, RestD...>::type OneDimensionDownArrayT;
    typedef OneDimensionDownArrayT type[PrimaryD];

    type data;

    OneDimensionDownArrayT& operator[] (int i) {
        OneDimensionDownArrayT& a = data[i];
        return a;
    }
}; 

int main () {

    array<int, 1, 2, 3> a7 {{{{1, 2, 3},{4, 5, 6}}}};
    a7[0][1][2] = 100; //=>won't recursively go through operator[]
                     //I want to recursively  obtain 0, 1 and 2 as index values

    a7[0][1][100] = 100; //also works correctly.
    std::cout << a7[0][1][100] << std::endl;

    return 0;
}
like image 762
alper Avatar asked Nov 02 '22 21:11

alper


1 Answers

The error here is actually a little subtle, change the line

   typedef typename array<T, RestD...>::type OneDimensionDownArrayT;

to

   typedef array<T, RestD...> OneDimensionDownArrayT;

The reason for this is because array<int, 1, 2, 3>::type equals array<int, 2, 3>::type which equals array<int, 3>::type which is int. In the end you end up with array<int, 1, 2, 3>::OneDimensionDownArrayT equal to int[2][3]. This is why you were only going down one level in your overloaded operator[], because it returns an actual array of ints. You can see this for yourself by adding in your main method:

        auto & e1 = a7[0];
        auto & e2 = e1[0];
        auto & e3 = e2[1];
        auto & e4 = e3[2];
        e4 = 100;

instead of accessing them all at once. Then step through with the debugger and check the types of e1-e4. e1 will have type int (&) [2][3] instead of array<int, 2, 3> &.

To allocate these on the heap instead of on the stack, in your class declare pointers to your OneDimensionDownArrayT instead of arrays of them, define constructors and a desctructor that will take care of allocating/deallocating your arrays. It might look something like this:

    template <class T, unsigned PrimaryD, unsigned ... RestD>
    struct array <T, PrimaryD, RestD...> {
        typedef typename array<T, RestD...>::type OneDimensionDownArrayT;

        array():data(new OneDimensionDownArrayT[PrimaryD]){}
        ~array() {
            delete[] data;
        }

        OneDimensionDownArrayT * data;

        OneDimensionDownArrayT& operator[] (int i) {
            OneDimensionDownArrayT& a = data[i];
            return a;
        }
    };

You will also want to define a copy constructor, move constructor and assignment operator for your class. This implementation will take much less space on the stack, but overall slightly more memory in total as you also need space for the pointers as well as the arrays themselves.

like image 177
SirGuy Avatar answered Nov 08 '22 03:11

SirGuy