5

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;
}
alper
  • 1,558
  • 2
  • 24
  • 54
  • So what's the problem? What behavior are you seeing? If it gives a compile error, what is it? What is the runtime behavior? What is your compiler and version? A little more information doesn't hurt, you know! – yzt May 20 '13 at 17:40
  • 3
    Have you considered... not doing this? Just use an `operator()` overload instead. Really, people try *way* too hard to shoehorn `operator[]` into multidimensional arrays. – Nicol Bolas May 20 '13 at 17:48
  • 5
    A couple of side notes: [Why shouldn't my Matrix class's interface look like an array-of-array?](http://www.parashift.com/c++-faq/matrix-array-of-array.html) and [I still don't get it. Why shouldn't my Matrix class's interface look like an array-of-array?](http://www.parashift.com/c++-faq/matrix-c-style-subscript.html) – n. 'pronouns' m. May 20 '13 at 17:49
  • The problem is that; there is no compilation or run time error. When I debug the code, on the call of a7[0][1][100] => it only one time intercepts the operator[] for the index '0' and It won't intercepts for the other indexes as '1' and '100'. I just won't to recursively obtain the indexes in the multi dimensional array I didn't use operator() because I am restricted to implement it on operator[]/ – alper May 20 '13 at 19:38
  • If you want to know why the debugger doesn't do what you want, you need to ask a question about the debugger. It looks like the program does what you want it to do. There's no problem understanding language features involved. There's only a problem understanding what the debugger does. Am I right? – n. 'pronouns' m. May 20 '13 at 20:36
  • you are right but at run time I won't able to check is the index is greater than the original array size. I can able to intercept it for the first dimension but for the remaining dimensions operator[] won't intercepts it. – alper May 20 '13 at 21:37

2 Answers2

1

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.

SirGuy
  • 10,222
  • 2
  • 32
  • 63
  • This Solution is inefficient for very large arrays like a1[1][2][3][4][5][6][7][8][9][10]. Because inside every object it creates another array object that when the dimension size increases memory usage will be so huge. – alper May 21 '13 at 04:08
  • No matter what you do, you need to allocate space for 10! objects. If you are running out of space on the stack, try using the heap (dynamically allocating the memory). – SirGuy May 21 '13 at 05:33
  • typedef array OneDimensionDownArrayT; works but under each dimension's object it creates and array object and it will consume unnessary memory. There is more than 10 objects because it i multidimensional array. how could I dynamically allocate => typedef array OneDimensionDownArrayT; – alper May 21 '13 at 07:14
  • 10! is ten factorial = 3628800 objects. For an `int` array that means 4 * 3628800 = 14.5MB of space. Programs are usually compiled to have a fixed amount of stack space (about 8MB for me, but there are no guarantees on this number) whereas the heap can access all available memory. The thing is that it's not just this implementation that will require this much memory, a regular, vanilla `int[1][2][3][4][5][6][7][8][9][10]` will also take 14.5MB on the stack. I've also updated my answer to an outline of how you would allocate memory on the heap instead of the stack. – SirGuy May 21 '13 at 17:19
1

If you want to use [] operators in succession an a multi-dimensional array, then each [] must return a (one less)-dimensional array.

If your multi-dimensional array type is:

template <class Type, int dim> MArray;

Then a MArray<SomeType, n>::operator[] must return a MArray<SomeType, n-1>. With a special case for either your special 1-D array returning an object (preferably reference) or a 2-D array returning a native 1-D array. This example used overly simplistic notation, but the key is that an n-D []-operator returns a (n-1)-D array.

John
  • 7,017
  • 2
  • 13
  • 22