How to prevent mutation of private structures returned by reference

Viewed 61

I have following code:

struct MyStruct {
    int a;
    int b;
};

class Foo {
public:
    MyStruct const &getValue() {
        return value;
    }

private:
    MyStruct value{10, 20};
};

int main() {
    Foo foo{};
    auto v = foo.getValue();
    v.a = 42; // compiles! it shouldn't!
}

I want to return a reference to private member, but don't allow caller to mutate contents of this reference. Just declaring return type to be MyStruct const & is not enough, as it probably defines the reference itself to be const, not the object reference points to. Though, declaring return type as MyStruct& const fails with "'const' qualifier may not be applied to a reference". I believe I might be missing something here.

I could have solved that with declaring MyStruct with all const fields, but I want to mutate it myself inside my class — only disallow caller to do so.

How is it possible to do so?

2 Answers

Just declaring return type to be MyStruct const & is not enough, as it probably defines the reference itself to be const, not the object reference points to.

That's wrong. References can't ever be reassigned anyway. This is the correct way to return a reference to an immutable object. However you actually copy the returned object:

auto v = foo.getValue();

and then modify the copy, which is a valid operation. You can declare v as reference

auto &v = foo.getValue();

to get the expected compiler error. Demo

Your wrong assumption is that auto v declares a reference. It doesn't, you get a copy of the MyStruct returned from getValue().

You can check that original object is not mutated:

int main() {
    Foo foo{};
    auto v = foo.getValue();
    v.a = 42; // compiles! it should!
    std::cout << foo.getValue().a; //prints 10
}

Declaring v as reference type wouldn't compile

int main() {
    Foo foo{};
    auto& v = foo.getValue(); // const is deduced by auto
    v.a = 42; // error
}
Related