distinguish between read and write using operator [] overloading in c++

2k views Asked by At

I got a Security Class that has an array of Predictions - Prediction is a Class, which holds only a double. I want to allow changing the value of the double, but allow only positive values, and when trying to read the double, if the value is uninitialized (equals -1 in my code) throw exception. I also have double operator in
Something like that:

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 do I define between reading and writing, because I'm doing different things when reading and writing.

THANKS

4

There are 4 answers

0
NathanOliver On

Using a combination of a conversion operator and a conversion constructor you can get this behavior. This sample code should give you an idea on how you need to implement you 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
0
dpmcmlxxvi On

A few points

  1. The predArray array should be a member of Security (not shown in sample code)

  2. The index access through the [] operator should be on an instance of Security and not predArray. The variable predArray is a raw array of objects, not the object holding 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;
  1. Add the positive value check on Prediction::operator double() and the Prediction::operator=(const double value) before the return statement;
2
davidhigh On

The central idea is the following: instead of returning a double&, you can return a proxy with an overloaded operator= (and everything else which is necessary). Then, the proxy performs the checking.

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 that 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 much more complex operations. For example: to notify the depending classes in the observer pattern.

0
Benjamin Lindley On

You cannot do that in operator[]. There's no way for the operator to know how the value it returns is going to be used. So you have to do this as a function of the returned object. You can handle the assignment to a negative value easy enough, by throwing in the assignment operator of the returned object.

Prediction::operator=(const double value){
    if (value < 0)
        throw something;
    ...
}

If you want this statement to throw:

double a = predArray[0];

You will have to do that in your conversion to double operator.

Prediction::operator double() const {
    if (value < 0)
        throw something;
    return value;
}