Search code examples
c++constantslanguage-lawyerconst-cast

Is modifying non-mutable member of non-const object in const method Undefined Behaviour?


dcl.type.cv provides an interesting example:

For another example,

struct X {
  mutable int i;
  int j;
};
struct Y {
  X x;
  Y();
};

const Y y;
y.x.i++;                                // well-formed: mutable member can be modified
y.x.j++;                                // ill-formed: const-qualified member modified
Y* p = const_cast<Y*>(&y);              // cast away const-ness of y
p->x.i = 99;                            // well-formed: mutable member can be modified
p->x.j = 99;                            // undefined: modifies a const member

which indicates that, via const_cast, one may modify mutable members of a const qualified object, while you can't do that with non-mutable members.

To my understanding, this is because of the original constness of y itself. What would happen if we got rid of the mutable keyword, the const qualifier fot y, but modified the fields in a const method?

Example below:

#include <vector>

struct foo {
    std::vector<int> vec{};

    void bar() const {
        auto& raw_ref = const_cast<std::vector<int>&>(vec);
        raw_ref.push_back(0);       // ok?

        auto* raw_this = const_cast<foo*>(this);
        raw_this->vec.push_back(0); // ok?
    }
};

int main() {
    foo f{};
    f.bar();
}

Does it exhibit Undefined Behaviour? I would think that it does not, since we're modifying an originally non-const, but in a const context.

Additionally, notice that I provided two ways of modifying the vec. One with non-const reference and one with non-const pointer to this (which was originally const in this constext due to foo::bar being a const method). Do they differ in any particular way, given the question's context? I would assume that both are okay here.

Disclaimer: I am aware of mutable keyword, but this desing (not only being flawed) is simply an example. One could assume that the author of the code wanted to prohibit every single way of modifying vec except for push_backs.


Solution

  • Your quoted paragraph actually spelled out exactly what is undefined [dcl.type.cv]

    Except that any class member declared mutable can be modified, any attempt to modify a const object during its lifetime results in undefined behavior.

    A const reference/pointer to a non-const object doesn't make that object a const object, all your accesses are well-formed.