Does casting away constness from “this” and then changing a member value invoke undefined behaviour?

依然范特西╮ 提交于 2019-12-22 05:10:39

问题


In a response to my comment to some answer in another question somebody suggests that something like

void C::f() const
{
  const_cast<C *>( this )->m_x = 1;
}

invokes undefined behaviour since a const object is modified. Is this true? If it isn't, please quote the C++ standard (please mention which standard you quote from) which permits this.

For what it's worth, I've always used this approach to avoid making a member variable mutable if just one or two methods need to write to it (since using mutable makes it writeable to all methods).


回答1:


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.




回答2:


There are two rules:

  1. You cannot modify a const object.

  2. You cannot modify an object through a const pointer or reference.

You break neither rule if the underlying object is not const. There is a common misunderstanding that the presence of a const pointer or const reference to an object somehow stops that object from changing or being changed. That is simply a misunderstanding. For example:

#include <iostream>
using namespace std;

// 'const' means *you* can't change the value through that reference
// It does not mean the value cannot change

void f(const int& x, int* y)
{
    cout << "x = " << x << endl;
    *y = 5;
    cout << "x = " << x << endl;
}

int main()
{
    int x = 10;
    f(x, &x);
}

Notice no casts, nothing funny. Yet an object that a function has a const reference to is modified by that function. That is allowed. Your code is the same, it just does it by casting away constness.

However, if the underlying object is const, this is illegal. For example, this code segfaults on my machine:

#include <iostream>
using namespace std;

const int i = 5;

void cast(const int *j)
{
    *const_cast<int *>(j) = 1;
}

int main(void)
{
    cout << "i = " << i << endl;
    cast(&i);
    cout << "i = " << i << endl;
}

See section 3.4.3 (CV qualifiers) and 5.2.7 (casting away constness).




回答3:


Without searching any further, § 1.9/4 in the C++11 Standard reads:

Certain other operations are described in this International Standard as undefined (for example, the effect of attempting to modify a const object).

And this is what you are trying to do here. It does not matter that you are casting away constness (if you didn't do it, the behaviour is well defined: your code would fail to compile). You are attempting to modify a const object, so you are running into undefined behaviour.

Your code will appear to work in many cases. But it won't if the object you are calling it on is really const and the runtime decided to store it in read-only memory. Casting away constness is dangerous unless you are really sure that this object was not const originally.



来源:https://stackoverflow.com/questions/14154382/does-casting-away-constness-from-this-and-then-changing-a-member-value-invoke

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!