104

Is it possible to overload [] operator twice? To allow, something like this: function[3][3](like in a two dimensional array).

If it is possible, I would like to see some example code.

3
  • 29
    Btw, it's much simpler and more common to overload operator()(int, int) instead... Commented Aug 14, 2011 at 1:23
  • 2
    Why recreat the wheel? Just use std::vector with a range constructor: stackoverflow.com/a/25405865/610351 Commented Aug 20, 2014 at 13:22
  • Or you can just use something like using array2d = std::array<std::array<int, 3>, 3>; Commented Apr 7, 2020 at 2:05

17 Answers 17

131

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.

Sign up to request clarification or add additional context in comments.

5 Comments

could use a destructor. And Proxy::operator[] should return int& not just int
Better to use std::vector<std::vector<int>> to avoid memleak and strange behavior on copy.
Both Boost's multi_array and extent_gen are good examples of this technique. boost.org/doc/libs/1_57_0/libs/multi_array/doc/…
However, const ArrayOfArrays arr; arr[3][5] = 42; will be able to pass compilation and changes arr[3][5], which is somehow different from what users' expectation that arr is const.
@abcdabcd987 That's not correct for a couple of reasons. First, Proxy::operator[] does not return a reference in this code (assuming your comment isn't in reply to Ryan Haining). More importantly, if arr is const then operator[] cannot be used. You would have to define a const version, and of course you would make it return const Proxy. Then Proxy itself would have const and non-const methods. And then your example would still not compile, and the programmer would be happy that all is well and good in the universe. =)
24

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.

2 Comments

Looks to me like the most practical and efficient solution. Wonder why it does not get more votes - maybe because it doesn't have the eye catching code.
@YigalReiss Or maybe because it doesn't show an example. Could be class A { private: int a[Y][X] ; public: int *operator[] (int index) { return a[index]; } }; Used as A mine ; mine[1][2] = 3;
22

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).

2 Comments

Why the overload of parentheses allows getting two inputs but you cannot do the same with the brackets?
@A.Frenzy Because: 1. overriding with 2 parameters would result in a call to myObj[2,3], not myObj[2][3]. and 2. The number of params the operator takes cannot be changed.The [] operator takes only one int, while () takes any number of parameters of any type.
16

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)).

Comments

11

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>

1 Comment

This becomes a lot more attractive with C++11 and brace initialization. Now you can write nValue = theArray[{2,3}];
6

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; } 

Comments

5

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]; } } 

Comments

4

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.

Comments

3
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.

Comments

2
#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; } 

Comments

2
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.

Comments

2

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; } }; 

Comments

2

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]; } }; 

Comments

1

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); } }; 

Comments

0

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.

Comments

0

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; } 

Comments

0

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.)

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.