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

+3


source to share


4 answers


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

      

+3


source


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

      

+1


source


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.

+1


source


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;

      

  1. Add positive value check to Prediction :: operator double () and Prediction :: operator = (const double value) before the return statement;
0


source







All Articles