I'm having trouble with telling the difference between get and set for the operator[]. I need to tell the difference between these function calls.
cout << data[5];
data[5] = 1;
I googled it, and the answers I found still didn't help. People suggested making the signatures for the methods different by adding const. I did that, and they still both called the same method.
There are the signatures I had used:
const T& operator[](unsigned int index) const;
T& operator[](unsigned int index);
What am I doing wrong?
The solution is to use a "proxy" object that will delay the actual operation:
#include <vector>
#include <iostream>
template<typename T>
struct MyArray {
std::vector<T> data;
MyArray(int size) : data(size) {}
struct Deref {
MyArray& a;
int index;
Deref(MyArray& a, int index) : a(a), index(index) {}
operator T() {
std::cout << "reading\n"; return a.data[index];
}
T& operator=(const T& other) {
std::cout << "writing\n"; return a.data[index] = other;
}
};
Deref operator[](int index) {
return Deref(*this, index);
}
};
int main(int argc, const char *argv[]) {
MyArray<int> foo(3);
foo[1] = 42;
std::cout << "Value is " << foo[1] << "\n";
return 0;
}
Simple const
-ness cannot be used because you may need to read from a non-const instance, that is the reason for which you must delay the operation: the assignment happens "after" the access and the compiler doesn't tell you if the access will be later used as a target for assignment or not.
The idea is therefore that on access you just store away the index that has been requested and wait to know if a reading or a writing operation is happening. By providing an implicit conversion operator from the proxy to T
you know when a reading operation occurs, by providing an assignment operator to the proxy from T
you know when writing occurs.
The const
version means that if the object on which it is called is const
, you are allowed to call that version of the []
operator, and only that version.
But if the object is not const
, then both versions of the []
operator can be called, but the compiler will select the non-const version. In other words, for a non-const object, the non-const version of the operator can act as either the "setter" or "getter". This is why the same version is called in both cases in your example, because your data
object is not const.
You would have to do something like this:
const Data& data_cref = data;
cout << data_cref[5]; // calls the const version of operator[]
Data& data_ref = data;
data_ref[5] = 1; // calls the non-const version of operator[]
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With