Distinguish read and write using operator [] overloading in C ++
I have a security class that has an array of predictions. Prediction is a class that only contains double. I want to allow the double value to be changed, but only allow positive values, and when trying to read the double, if the value is uninitialized (equal to -1 in my code) throw an exception. I also have a double operator in
Something like this:
class Prediction{
double value;
public:
.....
Prediction::operator double() const {
return this->prediction;
}
Prediction::operator=(const double value){
...
//check value
}
}
class Security{
...
Prediction& Security::operator[](int index){
return predArray[index];
}
}
Prediction *predArray = new Prediction[4];
//default constructor set the value -1;
double a = predArray[0] //should throw an exception, because predArray[0] = -1
predArray[0] = 4; //should be O.K. because I want to change the value
predArray[1] = -4; //should throw exception, because trying to put negative value;
where can I define between reading and writing, because I do different things when reading and writing.
thank
source to share
You cannot do this in operator[]
. There is no way for the operator to know how the return value will be used. Thus, you have to do it as a function of the returned object. You can easily pass the job to a negative value by executing the assignment command of the returned object.
Prediction::operator=(const double value){
if (value < 0)
throw something;
...
}
If you want this operator to throw:
double a = predArray[0];
You will need to do this when converting to double operator.
Prediction::operator double() const {
if (value < 0)
throw something;
return value;
}
source to share
By using a combination of a conversion operator and a conversion constructor, you can get this behavior. This code example should give you an idea of how you need to implement the classes:
class Foo
{
int value;
public:
Foo() { value = -1; }
Foo(int value) {
if (value < 0) cout << "error\n"; else { cout << "ok\n"; this->value = value; }
}
operator int() { if (value < 0) cout << "error\n"; else return value; }
};
class Bar
{
Foo * fooArray;
public:
Bar() { fooArray = new Foo[4]; }
Foo & operator [](int i) { return fooArray[i]; }
};
int main()
{
Bar test;
int foobar = test[0];
test[1] = 4;
test[2] = -4;
cin.get();
return 0;
}
Output:
error
ok
error
source to share
The central idea is this: instead of returning, double&
you can return the overloaded proxy operator=
(and whatever else is needed). The proxy then checks.
struct reference_proxy
{
reference_proxy(double &_d) : d(_d) {}
reference_proxy& operator=(double rhs) //operator= throws when rhs<0
{
if(rhs<0.0)
{
std::cout<<"throw exception"<<std::endl;
}
else
{
std::cout<<"ok"<<std::endl;
d = rhs;
}
return *this;
}
operator double () //cast to double gives an error when uninitialized
{
if(d<0.0)
{
std::cout<<"throw exception"<<std::endl;
}
return d;
}
// add further required functions like operator+= etc.
private:
double& d;
};
Then you can use this in your other classes:
struct Prediction
{
operator double& () { return d; }
double d = -1.0;
};
struct Security
{
template<typename ... Args>
Security(Args&& ... args) : v(std::forward<Args>(args) ...) {}
auto operator[](int i)
{
return reference_proxy(v[i]);
}
std::vector<Prediction> v;
};
Application:
int main()
{
Security s(10);
double a = s[0]; //prints "throw exception"
s[0] = 4; //prints "ok"
s[1] = -4; //prints "throw exception"
return 0;
}
DEMO .
Note that this scheme can also be used for more complex operations. For example: to notify dependent classes in an observer template.
source to share
Multiple points
-
Array predArray must be a safety member (not shown in the example code)
-
Index access via the [] operator must be in the Security instance, not predArray . The predArray variable is a raw array of objects, not an object containing your array.
For example:
Security o = new Security();
double a = o[0] //should throw an exception, because predArray[0] = -1
o[0] = 4; //should be O.K. because I want to change the value
o[1] = -4; //should throw exception, because trying to put negative value;
- Add positive value check to Prediction :: operator double () and Prediction :: operator = (const double value) before the return statement;
source to share