操作员[][]超载

有没有可能重载 []操作符两次? 为了允许这样的操作: function[3][3](类似于二维数组)。

如果可能的话,我希望看到一些示例代码。

63790 次浏览

It is possible if you return some kind of proxy class in first [] call. However, there is other option: you can overload operator() that can accept any number of arguments (function(3,3)).

An expression x[y][z] requires that x[y] evaluates to an object d that supports d[z].

This means that x[y] should be an object with an operator[] that evaluates to a "proxy object" that also supports an operator[].

This is the only way to chain them.

Alternatively, overload operator() to take multiple arguments, such that you might invoke myObject(x,y).

You can overload operator[] to return an object on which you can use operator[] again to get a result.

class ArrayOfArrays {
public:
ArrayOfArrays() {
_arrayofarrays = new int*[10];
for(int i = 0; i < 10; ++i)
_arrayofarrays[i] = new int[10];
}


class Proxy {
public:
Proxy(int* _array) : _array(_array) { }


int operator[](int index) {
return _array[index];
}
private:
int* _array;
};


Proxy operator[](int index) {
return Proxy(_arrayofarrays[index]);
}


private:
int** _arrayofarrays;
};

Then you can use it like:

ArrayOfArrays aoa;
aoa[3][5];

This is just a simple example, you'd want to add a bunch of bounds checking and stuff, but you get the idea.

You can use a proxy object, something like this:

#include <iostream>


struct Object
{
struct Proxy
{
Object *mObj;
int mI;


Proxy(Object *obj, int i)
: mObj(obj), mI(i)
{
}


int operator[](int j)
{
return mI * j;
}
};


Proxy operator[](int i)
{
return Proxy(this, i);
}
};


int main()
{
Object o;
std::cout << o[2][3] << std::endl;
}

It 'll be great if you can let me know what function, function[x] and function[x][y] are. But anyway let me consider it as an object declared somewhere like

SomeClass function;

(Because you said that it's operator overload, I think you won't be interested at array like SomeClass function[16][32];)

So function is an instance of type SomeClass. Then look up declaration of SomeClass for the return type of operator[] overload, just like

ReturnType operator[](ParamType);

Then function[x] will have the type ReturnType. Again look up ReturnType for the operator[] overload. If there is such a method, you could then use the expression function[x][y].

Note, unlike function(x, y), function[x][y] are 2 separate calls. So it's hard for compiler or runtime garantees the atomicity unless you use a lock in the context. A similar example is, libc says printf is atomic while successively calls to the overloaded operator<< in output stream are not. A statement like

std::cout << "hello" << std::endl;

might have problem in multi-thread application, but something like

printf("%s%s", "hello", "\n");

is fine.

One approach is using std::pair<int,int>:

class Array2D
{
int** m_p2dArray;
public:
int operator[](const std::pair<int,int>& Index)
{
return m_p2dArray[Index.first][Index.second];
}
};


int main()
{
Array2D theArray;
pair<int, int> theIndex(2,3);
int nValue;
nValue = theArray[theIndex];
}

Of course, you may typedef the pair<int,int>

For a two dimensional array, specifically, you might get away with a single operator[] overload that returns a pointer to the first element of each row.

Then you can use the built-in indexing operator to access each element within the row.

It is possible to overload multiple [] using a specialized template handler. Just to show how it works :

#include <iostream>
#include <algorithm>
#include <numeric>
#include <tuple>
#include <array>


using namespace std;


// the number '3' is the number of [] to overload (fixed at compile time)
struct TestClass : public SubscriptHandler<TestClass,int,int,3> {


// the arguments will be packed in reverse order into a std::array of size 3
// and the last [] will forward them to callSubscript()
int callSubscript(array<int,3>& v) {
return accumulate(v.begin(),v.end(),0);
}


};


int main() {




TestClass a;
cout<<a[3][2][9];  // prints 14 (3+2+9)


return 0;
}

And now the definition of SubscriptHandler<ClassType,ArgType,RetType,N> to make the previous code work. It only shows how it can be done. This solution is optimal nor bug-free (not threadsafe for instance).

#include <iostream>
#include <algorithm>
#include <numeric>
#include <tuple>
#include <array>


using namespace std;


template <typename ClassType,typename ArgType,typename RetType, int N> class SubscriptHandler;


template<typename ClassType,typename ArgType,typename RetType, int N,int Recursion> class SubscriptHandler_ {


ClassType*obj;
array<ArgType,N+1> *arr;


typedef SubscriptHandler_<ClassType,ArgType,RetType,N,Recursion-1> Subtype;


friend class SubscriptHandler_<ClassType,ArgType,RetType,N,Recursion+1>;
friend class SubscriptHandler<ClassType,ArgType,RetType,N+1>;


public:


Subtype operator[](const ArgType& arg){
Subtype s;
s.obj = obj;
s.arr = arr;
arr->at(Recursion)=arg;
return s;
}
};


template<typename ClassType,typename ArgType,typename RetType,int N> class SubscriptHandler_<ClassType,ArgType,RetType,N,0> {


ClassType*obj;
array<ArgType,N+1> *arr;


friend class SubscriptHandler_<ClassType,ArgType,RetType,N,1>;
friend class SubscriptHandler<ClassType,ArgType,RetType,N+1>;


public:


RetType operator[](const ArgType& arg){
arr->at(0) = arg;
return obj->callSubscript(*arr);
}


};




template<typename ClassType,typename ArgType,typename RetType, int N> class SubscriptHandler{


array<ArgType,N> arr;
ClassType*ptr;
typedef SubscriptHandler_<ClassType,ArgType,RetType,N-1,N-2> Subtype;


protected:


SubscriptHandler() {
ptr=(ClassType*)this;
}


public:


Subtype operator[](const ArgType& arg){
Subtype s;
s.arr=&arr;
s.obj=ptr;
s.arr->at(N-1)=arg;
return s;
}
};


template<typename ClassType,typename ArgType,typename RetType> struct SubscriptHandler<ClassType,ArgType,RetType,1>{
RetType operator[](const ArgType&arg) {
array<ArgType,1> arr;
arr.at(0)=arg;
return ((ClassType*)this)->callSubscript(arr);
}
};
#include<iostream>


using namespace std;


class Array
{
private: int *p;
public:
int length;
Array(int size = 0): length(size)
{
p=new int(length);
}
int& operator [](const int k)
{
return p[k];
}
};
class Matrix
{
private: Array *p;
public:
int r,c;
Matrix(int i=0, int j=0):r(i), c(j)
{
p= new Array[r];
}
Array& operator [](const int& i)
{
return p[i];
}
};


/*Driver program*/
int main()
{
Matrix M1(3,3); /*for checking purpose*/
M1[2][2]=5;
}

With a std::vector<std::vector<type*>>, you can build the inside vector using custom input operator that iterate over your data and return a pointer to each data.

For example:

size_t w, h;
int* myData = retrieveData(&w, &h);


std::vector<std::vector<int*> > data;
data.reserve(w);


template<typename T>
struct myIterator : public std::iterator<std::input_iterator_tag, T*>
{
myIterator(T* data) :
_data(data)
{}
T* _data;


bool operator==(const myIterator& rhs){return rhs.data == data;}
bool operator!=(const myIterator& rhs){return rhs.data != data;}
T* operator*(){return data;}
T* operator->(){return data;}


myIterator& operator++(){data = &data[1]; return *this; }
};


for (size_t i = 0; i < w; ++i)
{
data.push_back(std::vector<int*>(myIterator<int>(&myData[i * h]),
myIterator<int>(&myData[(i + 1) * h])));
}

Live example

This solution has the advantage of providing you with a real STL container, so you can use special for loops, STL algorithms, and so on.

for (size_t i = 0; i < w; ++i)
for (size_t j = 0; j < h; ++j)
std::cout << *data[i][j] << std::endl;

However, it does create vectors of pointers, so if you're using small datastructures such as this one you can directly copy the content inside the array.

struct test
{
using array_reference = int(&)[32][32];


array_reference operator [] (std::size_t index)
{
return m_data[index];
}


private:


int m_data[32][32][32];
};

Found my own simple solution to this.

Sample code:

template<class T>
class Array2D
{
public:
Array2D(int a, int b)
{
num1 = (T**)new int [a*sizeof(int*)];
for(int i = 0; i < a; i++)
num1[i] = new int [b*sizeof(int)];


for (int i = 0; i < a; i++) {
for (int j = 0; j < b; j++) {
num1[i][j] = i*j;
}
}
}
class Array1D
{
public:
Array1D(int* a):temp(a) {}
T& operator[](int a)
{
return temp[a];
}
T* temp;
};


T** num1;
Array1D operator[] (int a)
{
return Array1D(num1[a]);
}
};




int _tmain(int argc, _TCHAR* argv[])
{
Array2D<int> arr(20, 30);


std::cout << arr[2][3];
getchar();
return 0;
}
template<class F>
struct indexer_t{
F f;
template<class I>
std::result_of_t<F const&(I)> operator[](I&&i)const{
return f(std::forward<I>(i))1;
}
};
template<class F>
indexer_t<std::decay_t<F>> as_indexer(F&& f){return {std::forward<F>(f)};}

This lets you take a lambda, and produce an indexer (with [] support).

Suppose you have an operator() that supports passing both coordinates at onxe as two arguments. Now writing [][] support is just:

auto operator[](size_t i){
return as_indexer(
[i,this](size_t j)->decltype(auto)
{return (*this)(i,j);}
);
}


auto operator[](size_t i)const{
return as_indexer(
[i,this](size_t j)->decltype(auto)
{return (*this)(i,j);}
);
}

And done. No custom class required.

vector< vector< T > > or T** is required only when you have rows of variable length and way too inefficient in terms of memory usage/allocations if you require rectangular array consider doing some math instead! see at() method:

template<typename T > class array2d {


protected:
std::vector< T > _dataStore;
size_t _sx;


public:
array2d(size_t sx, size_t sy = 1): _sx(sx), _dataStore(sx*sy) {}
T& at( size_t x, size_t y ) { return _dataStore[ x+y*sx]; }
const T& at( size_t x, size_t y ) const { return _dataStore[ x+y*sx]; }
const T& get( size_t x, size_t y ) const { return at(x,y); }
void set( size_t x, size_t y, const T& newValue ) { at(x,y) = newValue; }
};

Using C++11 and the Standard Library you can make a very nice two-dimensional array in a single line of code:

std::array<std::array<int, columnCount>, rowCount> myMatrix {0};


std::array<std::array<std::string, columnCount>, rowCount> myStringMatrix;


std::array<std::array<Widget, columnCount>, rowCount> myWidgetMatrix;

By deciding the inner matrix represents rows, you access the matrix with an myMatrix[y][x] syntax:

myMatrix[0][0] = 1;
myMatrix[0][3] = 2;
myMatrix[3][4] = 3;


std::cout << myMatrix[3][4]; // outputs 3


myStringMatrix[2][4] = "foo";
myWidgetMatrix[1][5].doTheStuff();

And you can use ranged-for for output:

for (const auto &row : myMatrix) {
for (const auto &elem : row) {
std::cout << elem << " ";
}
std::cout << std::endl;
}

(Deciding the inner array represents columns would allow for an foo[x][y] syntax but you'd need to use clumsier for(;;) loops to display output.)

If, instead of saying a[x][y], you would like to say a[{x,y}], you can do like this:

struct Coordinate {  int x, y; }


class Matrix {
int** data;
operator[](Coordinate c) {
return data[c.y][c.x];
}
}

The shortest and easiest solution:

class Matrix
{
public:
float m_matrix[4][4];


// for statements like matrix[0][0] = 1;
float* operator [] (int index)
{
return m_matrix[index];
}


// for statements like matrix[0][0] = otherMatrix[0][0];
const float* operator [] (int index) const
{
return m_matrix[index];
}


};