I'm porting code that uses a very large array of floats, which may trigger malloc failures from c to c++. I asked a question about whether I should use vectors or deques and Niki Yoshiuchi generously offered me this example of a safely wrapped type:
template<typename T>
class VectorDeque
{
private:
enum TYPE { NONE, DEQUE, VECTOR };
std::deque<T> m_d;
std::vector<T> m_v;
TYPE m_type;
...
public:
void resize(size_t n)
{
switch(m_type)
{
case NONE:
try
{
m_v.resize(n);
m_type = VECTOR;
}
catch(std::bad_alloc &ba)
{
m_d.resize(n);
m_type = DEQUE;
}
break;
}
}
};
I needed a 2D vector of vectors/deque of deques, so I modified it to the following code:
template<typename T>
class VectorDeque
{
private:
enum STORAGE_CONTAINER { NONE, DEQUE, VECTOR };
std::deque<std::deque<T> > x_d,y_d,z_d;
std::vector<std::vector<T> > x_v,y_v,z_v;
TYPE my_container;
public:
void resize(size_t num_atoms, size_t num_frames)
{
switch(m_type)
{
case NONE:
try
{
x_v.resize(num_atoms);
for (unsigned int couter=0;couter < num_frames; counter++)
x_v[counter].resize(num_frames);
y_v.resize(num_atoms);
for (unsigned int couter=0;couter < num_frames; counter++)
y_v[counter].resize(num_frames);
z_v.resize(num_atoms);
for (unsigned int couter=0;couter < num_frames; counter++)
z_v[counter].resize(num_frames);
my_container = VECTOR;
}
catch(std::bad_alloc &e)
{
x_d.resize(num_atoms);
for (unsigned int couter=0;couter < num_frames; counter++)
x_d[counter].resize(num_frames);
y_d.resize(num_atoms);
for (unsigned int couter=0;couter < num_frames; counter++)
y_d[counter].resize(num_frames);
z_d.resize(num_atoms);
for (unsigned int couter=0;couter < num_frames; counter++)
z_d[counter].resize(num_frames);
my_container = DEQUE;
}
break;
}
}
};
I now want to be able to define my bracket operators so that I can have a statement like
x[1][2] directly access whichever is the real memory container I'm using (given by the value of my enumerated variable.
I've seen a couple of tutorials floating around about overriding the brackets operator, but have positively no idea to override double brackets.
How can you overload double brackets?
Additionally, how would you overload double iterators (in case I want to use an iterator, as opposed to direct indexing)?
EDIT 1:
Based on the solution from Martin York/Matteo Italia I devised the following class:
template<typename T>
class VectorDeque2D
{
public:
class VectorDeque2D_Inner_Set
{
VectorDeque2D& parent;
int first_index;
public:
// Just init the temp object
VectorDeque2D_Inner_Set(My2D& p, int first_Index) :
parent(p),
first_Index(first_index) {}
// Here we get the value.
T& operator[](int second_index) const
{ return parent.get(first_index,second_index);}
};
// Return an object that defines its own operator[] that will access the data.
// The temp object is very trivial and just allows access to the data via
// operator[]
VectorDeque2D_Inner_Set operator[](unsigned int first_index) {
return (*this, x);
}
void resize_first_index(unsigned int first_index) {
try {
my_vector.resize(first_index);
my_container = VECTOR;
}
catch(std::bad_alloc &e) {
my_deque.resize(first_index);
my_container = DEQUE;
}
}
void resize_second_index(unsigned int second_index) {
try {
for (unsigned int couter=0;couter < my_vector.size(); counter++) {
my_vector[counter].resize(second_index);
}
my_container = VECTOR;
}
catch(std::bad_alloc &e) {
for (unsigned int couter=0;couter < my_deque.size(); counter++) {
my_deque[counter].resize(second_index);
}
my_container = DEQUE;
}
}
void resize(unsigned int first_index,
unsigned int second_index) {
try {
my_vector.resize(first_index);
for (unsigned int couter=0;couter < my_vector.size(); counter++) {
my_vector[counter].resize(second_index);
}
my_container = VECTOR;
}
catch(std::bad_alloc &e) {
my_deque.resize(first_index);
for (unsigned int couter=0;couter < my_deque.size(); counter++) {
my_deque[counter].resize(second_index);
}
my_container = DEQUE;
}
}
private:
enum STORAGE_CONTAINER { NONE, DEQUE, VECTOR };
friend class VectorDeque2D_Inner_Set;
std::vector<std::vector<T> > my_vector;
std::deque<std::deque<T> > my_deque;
STORAGE_CONTAINER my_container;
T& get(int x,int y) {
T temp_val;
if(my_container == VECTOR) {
temp_val = my_vector[first_index][second_index];
}
else if(my_container == DEQUE) {
temp_val = my_deque[first_index][second_index];
}
return temp_val;
}
};
Finally a size-safe 2D container!! Thanks guys!
There are two main techniques:
1) Use operator() rather than operator[].
This is because the operator() allows multiple parameters.
class My2D
{
public:
int& operator()(int x,int y) { return pget(x,y);}
private:
int& pget(int x,int y) { /* retrieve data from 2D storage */ }
};
2) Use operator[] but return an intermediate object.
You can then apply the second operator[] to the intermediate object.
class My2D
{
public:
class My2DRow
{
My2D& parent;
int x;
public:
My2DRow(My2D& p, int theX) : parent(p), x(theX) {} // Just init the temp object
int& operator[](int y) const { return parent.pget(x,y);} // Here we get the value.
};
// Return an object that defines its own operator[] that will access the data.
// The temp object is very trivial and just allows access to the data via operator[]
My2DRow operator[](int x) { return My2DRow(*this, x);}
private:
friend class My2DRow;
int& pget(int x,int y) { /* retrieve data from 2D storage */ }
};
int main()
{
My2D data;
int& val = data[1][2]; // works fine.
// This is the same as
My2D::My2DRow row = data[1];
int& val2 = row[2];
}
I prefer the second technique.
This is because it leaves the original code untouched and more natural to read (in an array context). Of course you pay for the simplicity at the high level with slightly more complex code implementing your 2D array.
How can you overload double brackets?
I didn't fully understand your question, but you have to overload brackets, and make them return an object who overloads its own bracket operator.
For example, if you have a vector of vectors, the work is already done: vector < vector < something > > overloads operator[], which returns a vector< something >; this, in turn, has its bracket operator overloaded (and it returns a something object), so you can simply do:
vector<vector<something> > vec;
// ...
something s = vec[2][3];
Example with a proxy object:
template <typename T>
class Container
{
private:
// ...
public:
// Proxy object used to provide the second brackets
template <typename T>
class OperatorBracketHelper
{
Container<T> & parent;
size_t firstIndex;
public:
OperatorBracketHelper(Container<T> & Parent, size_t FirstIndex) : parent(Parent), firstIndex(FirstIndex) {}
// This is the method called for the "second brackets"
T & operator[](size_t SecondIndex)
{
// Call the parent GetElement method which will actually retrieve the element
return parent.GetElement(firstIndex, SecondIndex);
}
}
// This is the method called for the "first brackets"
OperatorBracketHelper<T> operator[](size_t FirstIndex)
{
// Return a proxy object that "knows" to which container it has to ask the element
// and which is the first index (specified in this call)
return OperatorBracketHelper<T>(*this, FirstIndex);
}
T & GetElement(size_t FirstIndex, size_t SecondIndex)
{
// Here the actual element retrieval is done
// ...
}
}
(add overloaded const methods wherever appropriate :) )
Note that with this method you lose almost nothing in respect to an operator() implementation, since the retrieval is still done in one single place, without constraints on the usage of the two indexes, having both indexes at the moment of performing the retrieval, and without returning "fat" temporary objects (OperatorBracketHelper is just as big as two pointers, and can be easily optimized away by the compiler).
There is no "double brackets" operator in C++. What you need to do is define a single [] operator and have it return a reference to another object, which can in turn respond to its own [] operator. This can be nested as many levels deep as you require.
For example, when you create a vector of vectors, the [] operator on the outer vector returns a reference to one of the inner vectors; the [] operator on that vector returns a reference to an individual element of the vector.
std::vector<std::vector<float> > example;
std::vector<float> & first = example[0]; // the first level returns a reference to a vector
float & second = example[0][0]; // the same as first[0]
Don't overload the [] operator, overload the () operator.
See this link:Overloading Subscript Operator.
I highly suggest reading through the C++ FAQ Lite at least once before posting to Stack Overflow. Also, searching Stack Overflow may yield some useful information also.
I covered overloading operator[] for a multi-dimensional array in an answer to a previous question.
I'd probably deal with iterators pretty similarly: Have one iterator that represents a "slice" (row or column) of the multi-dimensional array, and then another that represents an element in that slice.
Related
I need to use matrices and I am using this kind of declaration, for example for int data type:
std::vector < vector <int>> my2Dvec(rows, vector <int> (cols));
But now I would like to use it for multiple data types, so I declared this template:
template <typename T>
struct matrix
{
int col, row;
std::vector <std::vector <T>> data;
matrix(int c, int r) : col(c), row(r), data(col, std::vector <T> (row))
{
;
}
};
So I can use it as as:
matrix <int> m(10, 10);
...
m.data[1][2] = 0;
Now, how can I use (if possible) :
m[i][j] = someValue;
? How to implement such a feature ?
You just have to implement a [] operator that returns a reference to a row:
std::vector<T> & operator[](int i) {
return data[i];
}
As operator [] is already defined on a vector, that will be enough.
A point on encapsulation.
This can be used that way, because as it is declared as a struct, with a public implementation, there is no encapsulation here. If matrix has hidden its implementation (which is the normal C++ way), you should simply declare a Row<T> type that implements operator []. This is nothing more than duck typing and even if it is more used on dynamic languages like Python or Ruby, it can help even in C++.
Here is an example respecting the encapsulation through duck typing:
/*
class Matrix represents a matrix
operator[](int i) returns a reference to the ith Row
Row is an internal type that simply defines the operator[](int j) to return
the ith element in a Row (which is a T&)
*/
template <typename T>
class Matrix {
// implementation
int col, row;
typedef std::vector<T> Row;
std::vector<Row> data;
public: // interface
Matrix(int c, int r): row(r), col(c), data(c, std::vector<T>(r)) {}
// allow to use matrix[i][j]
Row & operator[](int i) {
return data[i];
}
};
You need to implement operator[] in a way that it'll return a proxy object on which you can use operator[] again to get a result.
Example code:
struct matrix
{
struct Proxy
{
std::vector<int>* vec;
Proxy(std::vector<int>* vec_)
: vec(vec_)
{
}
int& operator[](int index)
{
return (*vec)[index];
}
};
matrix(int c, int r) : col(c), row(r), data(c, std::vector<int>(r))
{
}
Proxy operator[](int index)
{
return Proxy(&data[index]);
}
int col, row;
std::vector<std::vector<int>> data;
};
You have to implement the array-subscript operator:
int& operator[](std::size_t i) { return data[i]; }
hello i want to create simple array class to get value and insert value like (array[0] = 4) and this is my program but i have problem to use [] = in same time for insert
template <typename Param>
class arr
{
private:
int Last_point = 0;
Param Data[];
public:
void& operator[]=(int Element_id, Param v)
{
Data[Element_id] = v;
}
Param& operator[] (int Element_id)
{
return Data[Element_id];
}
};
void main()
{
arr <int> Array;
Array[1] = 555;
cout << "Is(" << to_string(Array[1]) << ")" << endl;
system("pause");
}
is there any operator like ([]=)? or for this, i have to use which methods? also i want to get value if just used []
The syntax you are attempting for the operator[] functions is quite wrong. You need something like:
// Version for non-const objects
Param& operator[](std::size_t i)
{
return Data[i];
}
// Version for const objects
Param const& operator[](std::size_t i) const
{
return Data[i];
}
If you want to support arrays whose sizes are known at compile time, you can use:
template <typename Param, std::size_t N>
class arr
{
private:
Param Data[N];
public:
Param& operator[](std::size_t i)
{
return Data[i];
}
Param const& operator[](std::size_t i) const
{
return Data[i];
}
};
If you want to support arrays whose sizes are known at run time, you can use the following. However, you need to be aware of The Rule of Three and make sure to implement the copy constructor and the copy assignment operator properly.
template <typename Param>
class arr
{
private:
Param* Data;
public:
arr(size_t size) : Data(new Param[size]) {}
~arr() { delete [] Data; }
Param& operator[](std::size_t i)
{
return Data[i];
}
Param const& operator[](std::size_t i) const
{
return Data[i];
}
};
Foo& operator[] is sufficient for reading and writing.
Include the size of the array as a template parameter, or replace Param[] with std::vector<Param>.
Use size_t instead of int for array index types.
You don't need to wrap Array[1] in to_string for output.
Don't use void main! The standard says main must be int.
void& is not valid C++ either.
#include <iostream>
using namespace std;
template <typename Param, size_t Size> class arr {
private:
Param Data[Size];
public:
Param &operator[](size_t Element_id) {
return Data[Element_id];
}
};
int main() {
arr<int, 3> Array;
Array[1] = 555;
cout << "Is(" << Array[1] << ")" << endl;
}
However, all that arr does in my snippet is be a less useful std::array!
I am having trouble building a class that makes sure the user does not access an element that is off the end of the array, by building a class that mimics the behavior of an array, but adds a check. That is, this class will create a sequence of elements of a given type, and allow access to these elements with the [] bracket operator, but it will check to make sure that the user does not try to do something with an element that doesn't exist.
Here is the instructions on building it.
I have no idea how to make an index operator for this case. Please help me. Thanks!
Here is my 3 files I have so far...
dvd.h
class dvdArray{
dvd *elt;
int size;
static int defaultSieze;
int getSize();
void display();
dvdArray(unsigned int sz);
dvdArray();
dvdArray(dvdArray &obj);
~dvdArray();
};
dvd.cpp
dvd::dvd(){
id =0;
int n=5;
title = new char [n];
director = new char [n];
title[0] = '\0';
director[0] = '\0';
}
dvd::~dvd(void)
{
}
dvdArray::dvdArray(unsigned int sz){
elt = new dvd[sz];
}
dvdArray::dvdArray(){
size = defaultSieze;
elt = new dvd[defaultSieze];
}
dvdArray::dvdArray(dvdArray &obj){
size = obj.size;
elt = new dvd[defaultSieze];
for (int i=0; i!='\0'; ++i) {
elt[i]=obj.elt[i];
}
}
dvdArray::~dvdArray(void)
{
}
The easiest / cleanest thing to do is to derive from std::vector (or std::array if it suits your purposes better), which is safe as long as you don't then delete the object using a std::vector*, and as long as the functions you want to do checked access accept the parameter as a checked_vector and not a std::vector*/&...
Example:
template <typename T>
class checked_vector : public std::vector<T>
{
public:
// ...forwarding constructors
using std::vector::vector;
T& operator[](size_t n) { return at(n); }
const T& operator[](size_t n) const { return at(n); }
};
Note: that won't protect you from invalid use of iterators, such as incrementing them too far or adding an illegal offset to them.
If - for whatever reason - you're determined to use your own implementation...
dvd& dvdArray::operator[](size_t n)
{
if (n >= size)
throw std::runtime_error("invalid array index");
return dvd[sz];
}
const dvd& dvdArray::operator[](size_t n) const
{
if (n >= size)
throw std::runtime_error("invalid array index");
return dvd[sz];
}
I would do something simple.. General purpose array, bounds checked...
Not sure why you would need that however... Vectors are a very good alternative to arrays.
template <typename T> class myArray{
size_t size;
T *arr;
void allocate(size_t s){
if(s<1) arr = NULL;
else arr = new T[s];
size = s;
}
public:
myArray(){ allocate(10); } //default constructor
myArray(size_t s){ allocate(s); }
~myArray(){ if(arr!=NULL) delete[] arr; arr=NULL; }
T& operator[] (size_t s) {
if(s>=size) throw Error(); //or do whatever
else return arr[s];
}
};
I have written a templates class for storing multiple bools in an integer.
Right now, setting and getting each bool is done with explicit functions
bool isBitSet(int index)
{
return static_cast<bool>((block_ >> index) % 2)
}
void setBitOn(int index)
{
block_ |= 1 << index;
}
I believe that the following would work for getting a value, but how would setting work since we can't directly return a reference for a bit?
const bool operator [] (int index) const
{
return static_cast<bool>((block_ >> index) % 2);
}
The same is done in std::vector<bool> and in std::bitset in the standard library. As stated in the reference, std::vector<bool> it returns a proxy class that has its operators overloaded to act as an element of the vector.
You could to that as well.
For a user-friendly example see again the reference for a public interface, it is something like this:
template <class Allocator>
class vector<bool, Allocator> {
// ...
public:
class reference {
friend class vector;
reference();
public:
~reference();
operator bool() const;
reference& operator=(bool x);
reference& operator=(const reference&);
void flip();
};
// ...
};
To implement this class you should store a member pointer to your actual data block and a mask to operate with.
For a real example, in the g++ headers look for member class of std::vector<bool> called std::vector<bool>::_Bit_reference in the file bits/stl_bvector.h.
To clarify the OP with an example:
Let's say you have a class containing 320 bools. You could write it as:
class boolcontainer {
uint32_t data[10];
public:
//default ctor. to initialize the elements with zeros
boolcontainer() { for (int i = 0; i < 10; ++i) { data[i] = 0; } }
}
You want to add an operator[]. To add a const one is easy:
class boolcontainer {
uint32_t data[10];
public:
bool operator[](int i) const { return data[i/32] & (1 << (i%32)); }
}
to have a non-const one you need much more. First you need to create a class that represents a reference to your value. You must have some kind of pointer to where the value is stored and (in this case) you need a bitmask to specify one concrete bit. To be able to handle this as a bool& you need to add some operators, namely conversion to bool and operator=:
class reference {
uint32_t *dataptr;
uint32_t mask;
public:
//constructor just initializing members
reference(uint32_t *dataptr_, uint32_t mask_) : dataptr(dataptr_), mask(mask_) {}
//conversion to bool
operator bool() const {
//just like in the getter, but the bitmask is stored now locally
return *dataptr & mask;
}
//sets one single bit represented by mask to b
reference& operator=(bool b) {
if (b) {
*dataptr |= mask;
} else {
*dataptr &= ~mask;
}
return *this;
}
//TODO copy ctor., operator==, operator<
};
Note that the above struct will behave as a bool& -- reading from it reads the value from the data point represented by the pointer and the mask, and similarly, writing to it overwrites the bit at the represented location. I also wrote a constructor that initializes the members.
Now all you need is that your boolcontainer's operator[] should return an object of the above class:
class boolcontainer {
uint32_t data[10];
public:
boolcontainer() { for (int i = 0; i < 10; ++i) { data[i] = 0; } }
class reference {
... //see above
}
//keep the const version for efficiency
bool operator[](int i) const { return data[i/32] & (1 << (i%32)); }
//non-const version returns our reference object.
reference operator[](int i) { return reference(&data[i/32], 1 << (i%32)); }
};
And now some code to test it (prints only the first 40 values):
#include <iostream>
#include "boolcontainer.h"
void printboolcontainer(const boolcontainer &bc)
{
//note that this is the constant version
for (int i = 0; i < 40; ++i) {
std::cout << bc[i];
}
std::cout << std::endl;
}
int main()
{
boolcontainer bc;
printboolcontainer(bc);
bc[0] = true;
bc[3] = true;
bc[39] = true;
printboolcontainer(bc);
}
I'm porting code that uses a very large array of floats, which may trigger malloc failures from c to c++. I asked a question about whether I should use vectors or deques and Niki Yoshiuchi generously offered me this example of a safely wrapped type:
template<typename T>
class VectorDeque
{
private:
enum TYPE { NONE, DEQUE, VECTOR };
std::deque<T> m_d;
std::vector<T> m_v;
TYPE m_type;
...
public:
void resize(size_t n)
{
switch(m_type)
{
case NONE:
try
{
m_v.resize(n);
m_type = VECTOR;
}
catch(std::bad_alloc &ba)
{
m_d.resize(n);
m_type = DEQUE;
}
break;
}
}
};
I needed a 2D vector of vectors/deque of deques, so I modified it to the following code:
template<typename T>
class VectorDeque
{
private:
enum STORAGE_CONTAINER { NONE, DEQUE, VECTOR };
std::deque<std::deque<T> > x_d,y_d,z_d;
std::vector<std::vector<T> > x_v,y_v,z_v;
TYPE my_container;
public:
void resize(size_t num_atoms, size_t num_frames)
{
switch(m_type)
{
case NONE:
try
{
x_v.resize(num_atoms);
for (unsigned int couter=0;couter < num_frames; counter++)
x_v[counter].resize(num_frames);
y_v.resize(num_atoms);
for (unsigned int couter=0;couter < num_frames; counter++)
y_v[counter].resize(num_frames);
z_v.resize(num_atoms);
for (unsigned int couter=0;couter < num_frames; counter++)
z_v[counter].resize(num_frames);
my_container = VECTOR;
}
catch(std::bad_alloc &e)
{
x_d.resize(num_atoms);
for (unsigned int couter=0;couter < num_frames; counter++)
x_d[counter].resize(num_frames);
y_d.resize(num_atoms);
for (unsigned int couter=0;couter < num_frames; counter++)
y_d[counter].resize(num_frames);
z_d.resize(num_atoms);
for (unsigned int couter=0;couter < num_frames; counter++)
z_d[counter].resize(num_frames);
my_container = DEQUE;
}
break;
}
}
};
I now want to be able to define my bracket operators so that I can have a statement like
x[1][2] directly access whichever is the real memory container I'm using (given by the value of my enumerated variable.
I've seen a couple of tutorials floating around about overriding the brackets operator, but have positively no idea to override double brackets.
How can you overload double brackets?
Additionally, how would you overload double iterators (in case I want to use an iterator, as opposed to direct indexing)?
EDIT 1:
Based on the solution from Martin York/Matteo Italia I devised the following class:
template<typename T>
class VectorDeque2D
{
public:
class VectorDeque2D_Inner_Set
{
VectorDeque2D& parent;
int first_index;
public:
// Just init the temp object
VectorDeque2D_Inner_Set(My2D& p, int first_Index) :
parent(p),
first_Index(first_index) {}
// Here we get the value.
T& operator[](int second_index) const
{ return parent.get(first_index,second_index);}
};
// Return an object that defines its own operator[] that will access the data.
// The temp object is very trivial and just allows access to the data via
// operator[]
VectorDeque2D_Inner_Set operator[](unsigned int first_index) {
return (*this, x);
}
void resize_first_index(unsigned int first_index) {
try {
my_vector.resize(first_index);
my_container = VECTOR;
}
catch(std::bad_alloc &e) {
my_deque.resize(first_index);
my_container = DEQUE;
}
}
void resize_second_index(unsigned int second_index) {
try {
for (unsigned int couter=0;couter < my_vector.size(); counter++) {
my_vector[counter].resize(second_index);
}
my_container = VECTOR;
}
catch(std::bad_alloc &e) {
for (unsigned int couter=0;couter < my_deque.size(); counter++) {
my_deque[counter].resize(second_index);
}
my_container = DEQUE;
}
}
void resize(unsigned int first_index,
unsigned int second_index) {
try {
my_vector.resize(first_index);
for (unsigned int couter=0;couter < my_vector.size(); counter++) {
my_vector[counter].resize(second_index);
}
my_container = VECTOR;
}
catch(std::bad_alloc &e) {
my_deque.resize(first_index);
for (unsigned int couter=0;couter < my_deque.size(); counter++) {
my_deque[counter].resize(second_index);
}
my_container = DEQUE;
}
}
private:
enum STORAGE_CONTAINER { NONE, DEQUE, VECTOR };
friend class VectorDeque2D_Inner_Set;
std::vector<std::vector<T> > my_vector;
std::deque<std::deque<T> > my_deque;
STORAGE_CONTAINER my_container;
T& get(int x,int y) {
T temp_val;
if(my_container == VECTOR) {
temp_val = my_vector[first_index][second_index];
}
else if(my_container == DEQUE) {
temp_val = my_deque[first_index][second_index];
}
return temp_val;
}
};
Finally a size-safe 2D container!! Thanks guys!
There are two main techniques:
1) Use operator() rather than operator[].
This is because the operator() allows multiple parameters.
class My2D
{
public:
int& operator()(int x,int y) { return pget(x,y);}
private:
int& pget(int x,int y) { /* retrieve data from 2D storage */ }
};
2) Use operator[] but return an intermediate object.
You can then apply the second operator[] to the intermediate object.
class My2D
{
public:
class My2DRow
{
My2D& parent;
int x;
public:
My2DRow(My2D& p, int theX) : parent(p), x(theX) {} // Just init the temp object
int& operator[](int y) const { return parent.pget(x,y);} // Here we get the value.
};
// Return an object that defines its own operator[] that will access the data.
// The temp object is very trivial and just allows access to the data via operator[]
My2DRow operator[](int x) { return My2DRow(*this, x);}
private:
friend class My2DRow;
int& pget(int x,int y) { /* retrieve data from 2D storage */ }
};
int main()
{
My2D data;
int& val = data[1][2]; // works fine.
// This is the same as
My2D::My2DRow row = data[1];
int& val2 = row[2];
}
I prefer the second technique.
This is because it leaves the original code untouched and more natural to read (in an array context). Of course you pay for the simplicity at the high level with slightly more complex code implementing your 2D array.
How can you overload double brackets?
I didn't fully understand your question, but you have to overload brackets, and make them return an object who overloads its own bracket operator.
For example, if you have a vector of vectors, the work is already done: vector < vector < something > > overloads operator[], which returns a vector< something >; this, in turn, has its bracket operator overloaded (and it returns a something object), so you can simply do:
vector<vector<something> > vec;
// ...
something s = vec[2][3];
Example with a proxy object:
template <typename T>
class Container
{
private:
// ...
public:
// Proxy object used to provide the second brackets
template <typename T>
class OperatorBracketHelper
{
Container<T> & parent;
size_t firstIndex;
public:
OperatorBracketHelper(Container<T> & Parent, size_t FirstIndex) : parent(Parent), firstIndex(FirstIndex) {}
// This is the method called for the "second brackets"
T & operator[](size_t SecondIndex)
{
// Call the parent GetElement method which will actually retrieve the element
return parent.GetElement(firstIndex, SecondIndex);
}
}
// This is the method called for the "first brackets"
OperatorBracketHelper<T> operator[](size_t FirstIndex)
{
// Return a proxy object that "knows" to which container it has to ask the element
// and which is the first index (specified in this call)
return OperatorBracketHelper<T>(*this, FirstIndex);
}
T & GetElement(size_t FirstIndex, size_t SecondIndex)
{
// Here the actual element retrieval is done
// ...
}
}
(add overloaded const methods wherever appropriate :) )
Note that with this method you lose almost nothing in respect to an operator() implementation, since the retrieval is still done in one single place, without constraints on the usage of the two indexes, having both indexes at the moment of performing the retrieval, and without returning "fat" temporary objects (OperatorBracketHelper is just as big as two pointers, and can be easily optimized away by the compiler).
There is no "double brackets" operator in C++. What you need to do is define a single [] operator and have it return a reference to another object, which can in turn respond to its own [] operator. This can be nested as many levels deep as you require.
For example, when you create a vector of vectors, the [] operator on the outer vector returns a reference to one of the inner vectors; the [] operator on that vector returns a reference to an individual element of the vector.
std::vector<std::vector<float> > example;
std::vector<float> & first = example[0]; // the first level returns a reference to a vector
float & second = example[0][0]; // the same as first[0]
Don't overload the [] operator, overload the () operator.
See this link:Overloading Subscript Operator.
I highly suggest reading through the C++ FAQ Lite at least once before posting to Stack Overflow. Also, searching Stack Overflow may yield some useful information also.
I covered overloading operator[] for a multi-dimensional array in an answer to a previous question.
I'd probably deal with iterators pretty similarly: Have one iterator that represents a "slice" (row or column) of the multi-dimensional array, and then another that represents an element in that slice.