Overloading multi-dimensional brackets [duplicate]

2020-05-06 08:15发布

问题:

This question already has answers here:
Closed 4 years ago.

How can I overload multi-dimensional brackets?

Let's say that I have a class which enables me to access points in an n-vector space. For example:

class NSpaceVector {
    private:
        int vectorSpace[8][8][8][8][8][8][8];
    public:        
        const NSpaceVector operator[][][][][][][](int i, int j, int k, int l, int m, int n, int p)const {return vectorSpace[i][j][k][l][m][n][p]; }
        NSpaceVector operator[][][][][][][](int i, int j, int k, int l, int m, int n, int p) {return vectorSpace[i][j][k][l][m][n][p]; }
}
unsigned long & operator [](int i) {return registers[i];}

What I would like to do is overload the bracket operator so that I can index into this location like so:

int main() {
    NSpaceVector nsv; // Assume initializes all to 0
    nsv[2][4][7][4][0][7][6] = 2;
    cout << nsv[2][4][7][4][0][7][6] << endl; //-> 2
    cout << nsv[1][4][7][4][0][7][6] << endl; //-> 0
    return 0;
}

I can't get this to compile. Any ideas? Thx, Keith :^)

回答1:

The standard answer is to have each [] (from left-to-right) return a proxy for the correct nested subset, the last of which actually returns a reference to the data. In your scheme, it would be easiest by far to template the nested types to automate production.

As the mysterious user4581301 mentions, it's much simpler to provide

nsv(2,4,7,4,0,7,6)

Sample template production of your desired class is probably simpler than using a temporary proxy object:

template <size_t Width, size_t Depth, typename T=int>
struct SubSpace;

template <size_t Width, typename T>
struct SubSpace<Width,0,T> {
    std::array<T, Width> array;

    T& operator[] (size_t i) { return array[i]; }
    T const& operator[] (size_t i) const { return array[i]; }
};

template <size_t Width, size_t Depth, typename T>
struct SubSpace {
    using Nested = SubSpace<Width, Depth-1, T>;
    std::array<Nested, Width> array;

    Nested& operator[] (size_t i) { return array[i]; }
    Nested const& operator[] (size_t i) const { return array[i]; }
};


using NSpaceVector = SubSpace<8,6>;

int main()
{
    NSpaceVector nsv;
    nsv[2][4][7][4][0][7][6] = 2;
    cout << nsv[2][4][7][4][0][7][6] << endl; //-> 2
}

Note this doesn't currently default-initialize the array members, but we can use aggregate initialization anyway:

    NSpaceVector nsv {}; // value-initialize every element
    nsv[2][4][7][4][0][7][6] = 2;
    cout << nsv[2][4][7][4][0][7][6] << endl; //-> 2
    cout << nsv[2][4][7][4][0][7][5] << endl; //-> 0

Also note that SubSpace<8,6> gives the 8x7 result you wanted, since Depth terminates at 0. This could be cleaned up with a toplevel wrapper, but I'm reluctant to terminate on Depth==1 instead and have everything go wrong when someone instantiates SubSpace<0,0> by accident.


Simpler still, depending on whether you want any behaviour in your class, is this:

struct OtherSpaceVector {
    int s[8][8][8][8][8][8][8];

    auto operator[] (size_t i) -> decltype(s[i]) { return s[i]; }
};

int main()
{
    OtherSpaceVector osv{};
    osv[2][4][7][4][0][7][6] = 2;
    std::cout << osv[2][4][7][4][0][7][6] << '\n'; 
    std::cout << osv[2][4][7][4][0][7][5] << '\n';
}

It's harder to alter the dimensions and feels more brittle than the template version, but it still works for the use case you provided.



回答2:

Any ideas?

There's only the option to overload the operator[]() for every level, and let the return types have another operator[]() overload.


As mentioned in comments, such is usually solved with overloading the call operator():

int operator()(size_t dim1, size_t dim2, ... size_t dimn);