How to assign value to left side using Overload operator[]? - c++

I have made the following code:-
class A{
bool bFlag[2];
public:
A(){
for(int i = 0; i < 2; i++)
bFlag[i] = false;
}
bool operator[](int r){ //i know how to assign value to R.H.S using operator[]
if( r >= 0 || r < 2 ){
bFlag[r] = true;
return bFlag[r];
}
return false;
}
};
int main(){
A obj;
bool x;
x = obj[0]; //this i know
//obj[1] = x; //how to do this is my doubt?
return 0;
}
I dont know to set value to L.H.S using operator[].
Please guide me to how to set x value to obj[1]

To use [] as an lvalue your overloaded [] should return by reference.
Is it okay to return reference to a private member?
Yes, It is perfectly fine.
Most of the STL classes do that if you see the STL.
The rule is you should return a const reference to your private member if you do not wish the user to modify the contents there or if you want to allow users to modify it you can return a non const reference.
You basically hide(Abstract) the details of your class from the user of class by making them private, but you still provide the functionality to be able to modify individual elements.

Als is right, but here is an example:
class A{
private:
int val[10];
public:
A(){}
int& operator[](int i) {
return val[i];
}
};
this makes in posible to do things like
A a;
a[2] = 2;
you should of course add the old method as well. to optimize for l-value to making it.
class A{
private:
int val[10];
public:
A(){}
const int operator[](int i) const {
return val[i];
}
int& operator[](int i) {
return val[i];
}
};

Related

Overloading double subscript operator of std::array [duplicate]

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.

Determine lvalue and rvalue in C++ function

UPDATE: I revised some place, and now the problem has changed in some way.
I'm writing a C++ class. Like:
class qqq{
map<int,int> core;
//......
int& operator[](int n){return core[n];};
};
int main(){
qqq a;
a[3]=7;a[5]=0;//Case a
int b=a[3];//Case b
return 0;
}
Although case A and case B are calling the same function(overloaded operator), but case a is used as an lvalue while case b is used as a rvalue.
For some reason, I want to have the effect that if 0 is passed to a[5], delete the node 5 in core. Like:
int& operator[](int n){
if(CASE A && THE VALUE PASSED TO IT IS 0)
core.erase(core.find(n));
else
return core[n];
}
Maybe my description is not accurate.
Here is an implementation of the proxy pattern mentioned in the comments.
Personally, I don't use this, my maps are wrapped in classes that don't provide operator[] at all, instead there are functions like .get(key, default) .init(key), .setdefault(key, default), etc. depending on the class.
// This code is C++11 but it's not essential to the problem.
// The current code calls copy constructors more than necessary.
#include <map>
#include <cassert>
template<class K, class V>
struct zero_map
{
struct proxy
{
std::map<K, V> *container;
K key;
operator V()
{
auto it = container->find(key);
if (it == container->end())
return V();
return *it;
}
void operator = (V value)
{
if (value == V())
{
container->erase(key);
}
else
{
// probably should use .insert() and conditionally assign
(*container)[key] = value;
}
}
};
std::map<K, V> _inner;
proxy operator[](K k)
{
return proxy{&_inner, k};
}
};
int main()
{
zero_map<int, int> foo;
assert (foo._inner.size() == 0);
foo[1] = 0;
assert (foo._inner.size() == 0);
foo[0] = 1;
assert (foo._inner.size() == 1);
foo[0] = 0;
assert (foo._inner.size() == 0);
}
As a comment said, use a proxy class.
template<typename T, size_t BadIndex>
class Element{ // please use a more meaningful name
public:
Element(const size_t index): index(index){}
operator T& (){return value;}
operator T const&() const{return value;}
T &operator =(const T &rhs){
if(index != BadIndex)
value = rhs;
return value;
}
operator T const&() const{return value;}
operator T&(){return value;}
private:
T value;
const size_t index;
};
class qqq{
public:
std::map<int, Element<int, 5>> core;
Element<int> &operator [](size_t index){
auto itt = core.find(index);
if(itt == core.end()){
core.emplace(index, index);
itt = core.find(index);
}
return (*itt).second;
}
};
That should work, but 5 will always give you a garbage result.
You have to always return a value which can be used as left value in the assignment expression. Therefore, I suggest to use a garbage int variable. I declared the garbage as static because we need just one instance of this variable and we don't care its value.
For example,
class qqq{
static int garbage;
map<int,int> core;
//......
int& operator[](int n){
if(CASE A && THE VALUE PASSED TO IT IS 0)
return garbage;
else
return core[n];
}
};
However, this solution is confusing in my point of view because the behaviour completely changes according to what you specify in the square brackets. If the value passed in input is incorrect, I would probably thrown an exception.
* EDIT *
I think you are over complicating the problem using the [] operator. You can easily solve your problem by using setter and getters. For example :
int set(int index, int value){
if( value == 0)
core.erase(core.find(index));
else
return core[index];
}
int get(int index) {
return core[index];
}
The [] allows only for returning a reference, you don't know what is the value used in the assignment.
You question is now clear, unfortunately you will have no way to do that is C++. operator[] is not a getter and a setter : it can only return a reference, and that reference is than used for a mere assignement. At the moment the operator returns its reference, you cannot know what value will be used for a assignement, and you can hardly know how the ref will be used.
IMHO what you need is more :
int getCore(int i) {
return core[i];
}
void setCore(int i, int newval) {
if (newval == 0) {
core.erase(core.find(i));
}
else {
core[i] == newval;
}

Overloading subscript operator for non-array elements

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

overload operator[] on return type

There is something that is troubling my brain since a moment: I am trying to overload the [] operator based on the return type. Here is what I need to do:
class A {
private:
double* data_;
int N_;
public:
A (N=0):N_(N){
data_ = new double[N];
}
~A {delete[] data_;}
double operator[] (const int i) {
return data_[i];
}
double* operator[] (const int i) {
return &data[i]; // for example; in fact here i need to return some block of data_
}
};
This code won't compile; and that is my problem. Can someone help me to solve this problem?
PS: I know how to overload normal functions on the return type for example:
int foo ();
string foo ();
I used some tricks that I read in this forum. In this way:
struct func {
operator string() { return "1";}
operator int() { return 2; }
};
int main( ) {
int x = func(); // calls int version
string y = func(); // calls string version
double d = func(); // calls int version
cout << func() << endl; // calls int version
func(); // calls neither
}
Thank you.
Two method overloads must have different signatures. The return type is not part of the signature of a method.
You can use the same "trick" that you use for functions, that is use a proxy object with conversion operators:
class A
{
private:
double* data_;
int N_;
public:
A (int N = 0)
: N_(N), data_(new double[N])
{}
~A() { delete[] data_; }
struct proxy
{
int i;
double * data;
operator double() const
{
return data[i];
}
operator double*()
{
return &data[i];
}
operator double const *() const
{
return &data[i];
}
};
proxy operator[] (int const i) {
proxy p { i, data_ };
return p;
}
proxy const operator[] (int const i) const {
proxy p { i, data_ };
return p;
}
};
int main()
{
{
A a(12);
double d = a[0];
double * pd = a[0];
}
{
A const ca(12);
double d = ca[0];
//double * pd = ca[0]; // does not compile thanks to overloads on const
double const * pcd = ca[0];
}
}
However, I would argue that this is a terrible idea. Having your operator[] return either a value or a pointer to this value is guaranteed to confuse the users of your class, in addition to making it impractical to use in expressions where both types are possible. For instance, std::cout << a[0]; would not compile (ambiguous overloads).
Probably you need something like that:
class A {
private:
double* data_;
int N_;
... // other stuff
public:
double operator[] (const int i) const { // note const here
return data_[i];
}
double& operator[] (const int i) { // note reference here
return data_[i];
}
};
also operator should be public to have a sense.

How Do I define a Double Brackets/Double Iterator Operator, Similar to Vector of Vectors'?

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.