Check C++ reference setter value

87 Views Asked by At

I have a question about the much-commented-about C++ reference getter-setter in C++. This is the base code:

class Foo
{
     X x_;
public:
          X & x()       { return x_; }
    const X & x() const { return x_; }
}

My issue with this is: what if I need to check the value when I set it? Let's imagine that I'm setting the latitude of a coordinate. If I were writing a setLat method, it would look like this:

void MyClass::setLat(double lat)
{
    if (lat < -90 || lat > 90)
    {
        throw std::range_error("The latitude must be between -90 and 90.");
    }
    _lat = lat;
}

Can I do something similar with:

double & MyClass::lat()
{
    return _lat;
}

Thanks

When I saw the code at the top of this question as the answer of another StackOverflow question, I thought, "Cool." But then, I ended up realizing what people here are saying: it breaks encapsulation. This member might just as well be public.

Since I don't use C++ that often, I wasn't too sure, thought.

The answer given by @ted-lyngmo did however make a lot of sense. I think that just having a regluar getter and a setter might in the end be the best. But I thought it might be possible to mimic the property ability of C# and Python in recent versions of C++. It looks like it is. Now: is it a good idea to do it? The question still seems open.

2

There are 2 best solutions below

3
Ted Lyngmo On BEST ANSWER

One option is to make a user-defined type of latitude and put all validations in that:

class Latitude {
public:
    Latitude() = default;
    
    explicit Latitude(double lat) : m_latitude(lat) {
        if (m_latitude < -90 || m_latitude > 90)
            throw std::range_error("The latitude must be between -90 and 90.");
    }

    explicit operator double() const { return m_latitude; }

private:
    double m_latitude = 0.;
};

That way you could keep the class containing a latitude clean:

class MyClass {
public:
    void setLat(Latitude lat) {
        _lat = lat; 
    }

private:
    Latitude _lat;
};

or

class MyClass {
public:
    Latitude& latitude() {
        return _lat;
    }

private:
    Latitude _lat;
};
2
463035818_is_not_an_ai On

Returning a non-const reference breaks encapsulation. The caller can do anything they like once they have the reference. The behavior you want can be achieved by returning a proxy:

#include<iostream>

struct foo {
    struct proxy {
        proxy(int& _value) : _value(_value) {}
        proxy& operator=(int value) {
            if (value != 42) throw "bad value";
            _value = value;
            return *this;
        }
        operator int () { return _value; }
        private:
        int& _value;
    };
    proxy get() { return {x}; }
    int get() const { return x; }
    private:
    int x = 0;
};


int main(){
    foo f;
    f.get() = 42;
    std::cout << f.get();
}

Live Demo

However, it is much simpler to provide an actual setter foo::set(int).