8

Imagine the following example:

class A
{
public:
    void doSomeStuff() { std::cout << "SomeStuff" << std::endl; }
};

class B
{
public:
    B(A& a) : a(a) {}
    void constStuff() const { a.doSomeStuff(); }

private:
    A &a;
};

If doSomeStuff() would change the data, wouldn't that affect class B as well? Why is such behaviour allowed?

Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
Quest
  • 2,764
  • 1
  • 22
  • 44
  • 1
    The misleading part here is an assumption that `const` qualifier on function somehow protects the object from modification. It does not, it is just an overload resolution mechanism. Basically inside of method there is no way to express that `this` object is immutable. Note that this also cripples a lot of possible compilation optimizations making C++ not as fast as some sources claim it is. – user7860670 Aug 08 '19 at 11:22
  • 1
    Consider that the member variable `a` is, to the effects of `const`, like a pointer. You cannot change the pointer itself, but you can change the pointed object. In the case of references, since [rebinding references is not allowed](https://stackoverflow.com/q/27037744/1782792), `const` makes no actual difference. – jdehesa Aug 08 '19 at 11:30
  • @VTT that's not really the issue here. It's more that `const` isn't propagated through references (or pointers). It would be nice if there were a wrapper for references like [`std::experimental::propagate_const`](https://en.cppreference.com/w/cpp/experimental/propagate_const) – Caleth Aug 08 '19 at 11:33
  • @Caleth `const` on references does not protect referenced object from modification either. – user7860670 Aug 08 '19 at 11:36
  • @VTT ? Yes, in the sense that it can be `const_cast` away, or the object can have `mutable` members. No, in that as a *programmer aid* `const` really does help in avoiding *accidentally* modifying something. Or are you talking about aliasing and concurrent modification? – Caleth Aug 08 '19 at 11:39
  • @Caleth Referenced object can always be modified without `const_cast` or `mutable` parts through aliasing. In context of OP's question this means that after invoking `a.doSomeStuff();` `B` instance could've been changed even though we are inside of `const` qualified method which will mean that invoking `b.constStuff()` could change `b`. That is `const` qualifier alone does not tell us much about whether object is actually changing or not. – user7860670 Aug 08 '19 at 11:52

4 Answers4

5

If doSomeStuff() would change the data, wouldn't that affect class B as well?

Well, not in the way a compiler checks for const correctness. A B holds a reference to an A. That object can reside anywhere, but most importantly it doesn't reside inside the B object. So modifying it does not do something with undefined behavior like changing a const object. We have a reference to a non-const object, so it's possible to modify the object via the reference. That's as far as the C++ type system cares, whether or not the object is maybe physically const.

It will probably affect the logical state of the B, but it is the responsibility of the programmer to ensure the class invariants hold. C++ will not hold your hand in that endeavor.

StoryTeller - Unslander Monica
  • 165,132
  • 21
  • 377
  • 458
2

The original object of the class A will be changed.

When you are using a const member function then the function deals with const T *this where T is the class type.

That is data members of the object are considered constant.

For a referenced type it could look like

A & const a;

However references themselves can not be constant.

That is for example this declaration

int x;

int & const rx = x;

is invalid and does not mean the same as

const int & rx = x;

So the class B has a reference that points to a non-constant object and using the reference the object can be changed.

Compare with the following declaration of the class B

class B
{
public:
    B(A * a) : a(a) {}
    void constStuff() const { a.doSomeStuff(); }

private:
    A *a;
};

Then then a constant member function is used the data member is considered like

A * const a;

(pointers themselves may be constant) that is the pointer itself that is constant not the object pointed to by the pointer and you can not change the pointer itself but you can change the object pointed to by the pointer.

Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
1

The const-qualified this pointer that's present in a const-method protects data changes from members inside B. B holds a reference to A so the object a references does not reside inside B and thus changes are allowed. This const qualification of this does not pass on to the reference.

Hatted Rooster
  • 35,759
  • 6
  • 62
  • 122
-1

If you would want to avoid that, you're supposed to make the reference to the helper class const:

class B
{
public:
    B(const A& a) : a(a) {}
    void constStuff() const { /* Invalid call: a.doSomeStuff(); */ }

private:
    const A &a;
};
Murphy
  • 3,827
  • 4
  • 21
  • 35