It is undefined behavior to (attempt to) modify a const object (7.1.6.1/4 in C++11).
So the important question is, what is a const object, and is m_x
one? If it is, then you have UB. If it is not, then there's nothing here to indicate that it would be UB -- of course it might be UB for some other reason not indicated here (for example, a data race).
If the function f
is called on a const instance of the class C
, then m_x
is a const object, and hence behavior is undefined (7.1.6.1/5):
const C c;
c.f(); // UB
If the function f
is called on a non-const instance of the class C
, then m_x
is not a const object, and hence behavior is defined as far as we know:
C c;
const C *ptr = &c;
c->f(); // OK
So, if you write this function then you are at the mercy of your user not to create a const instance of C
and call the function on it. Perhaps instances of C
are created only by some factory, in which case you would be able to prevent that.
If you want a data member to be modifiable even if the complete object is const
, then you should mark it mutable
. That's what mutable
is for, and it gives you defined behavior even if f
is called on a const instance of C
.
As of C++11, const
member functions and operations on mutable
data members should be thread-safe. Otherwise you violate guarantees provided by standard library, when your type is used with standard library functions and containers.
So in C++11 you would need to either make m_x
an atomic type, or else synchronize the modification some other way, or as a last resort document that even though it is marked const, the function f
is not thread-safe. If you don't do any of those things, then again you create an opportunity for a user to write code that they reasonably believe ought to work but that actually has UB.