Marius Küpper
Marius Küpper

Reputation: 267

Multidimensional array: operator overloading

I have a class with a multidimensional array:

example:

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

but array is not a vector of pointer which lead to other vectors etc...

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

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

The error i receive: "[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::B A::operator[] (std::ptrdiff_t i) {
    B res;
  if (dimensions <= 1){
    res = C();
}
  else{
    res = B();
  }
  dimensions--;
  return res;
}

A::B A::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;
}

Upvotes: 7

Views: 2283

Answers (3)

iwolf
iwolf

Reputation: 1062

It could be fun to use a Russian-doll style template class for this.

// 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's 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 keeps an array of previous-dimension elements. Dimension 1 uses the base specialization that tracks a 1D int array. Dimension 2 would then keep an array of one-dimentional arrays, D3 would have an array of two-dimensional arrays, etc. Then access looks the same as native multi-dimensional arrays. I'm using arrays inside the class in my example. This makes all the memory contiguous for the n-dimensional arrays, and doesn't require dynamic allocations inside the class. However, you could provide the same functionality with dynamic allocation as well.

Upvotes: 2

vsoftco
vsoftco

Reputation: 56557

The operator[] is evaluated from left to right in obj[1][1]...[1], so obj[1] returns a B object. Suppose now you just have int res = obj[1], then you'll assign to a B object (or C object in the case of multiple invocations of []) an 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, as overloaded operators are not inherited.

I got rid of your compiling error just by writing such operators for A and B (of course I have linking errors since there are un-defined functions).

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

Hope this makes sense.

PS: see also @Oncaphillis' comment!

Upvotes: 4

BWG
BWG

Reputation: 2288

vsoftco is totally right, you need to implement an overload operator if you want to actually access your elements. This is necessary if you want it to be dynamic, which is how you describe it. I actually thought this was an interesting problem, so I implemented what you described as a template. I think it works, but a few things might be slightly off. Here's 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. That means that in fact an array of arrays COULD be an array of elements! When you want to get a value, it tries to cast it to an element, and if that doesn't work, it just throws an assertion error.

Hopefully it makes sense, and of course if you have any questions ask away! In fact, I hope you do ask because the scope of the problem you describe is greater than you probably think it is.

Upvotes: 3

Related Questions