3

My question is inspired by this answer to another one of my questions: https://stackoverflow.com/a/56989169/2492801.

If I have an actually non-const object, but call one of its const methods, then inside the method this is const of course. If I const_cast away its constness and pass it to another method that performs a write operation on the object pointed to by this, is that undefined behaviour?

I wouldn't be surprised if it was, because this is really const inside a const method. On the other hand, the object itself is non-const so write operations are not generally forbidden.

For me it is important to know that to know how to deal with the problem described in my other question. Thank you!

Benjamin Bihler
  • 1,200
  • 7
  • 21
  • If the method needs to change state it shouldn't be const to begin with. – tkausl Jul 11 '19 at 12:57
  • 1
    If you check the other question, you will understand my reason for that... – Benjamin Bihler Jul 11 '19 at 12:57
  • Conciser using `mutable` (_"...permits modification of the class member declared mutable even if the containing object is declared const...."_) see: https://en.cppreference.com/w/cpp/language/cv – Richard Critten Jul 11 '19 at 13:06
  • @RichardCritten I don't want to modify my class members, see the other question... – Benjamin Bihler Jul 11 '19 at 13:08
  • Would it be possible subclass `QMessageBox` and make it `NiceBox(const QWidget* parent = nullptr) : QMessageBox(nullptr) { ... }` - which reads the parent position to position itself - but doesn't store the parent for other `NiceBox` functions to mess with? – Ted Lyngmo Jul 11 '19 at 13:12
  • @TedLyngmo Your commit fits better to the other question. But the answer is no, since as soons as I pass `nullptr` as parent to the message box, it is not centered with respect to the current dialog anymore, which is bad. – Benjamin Bihler Jul 11 '19 at 13:15
  • @BenjaminBihler I see. I don't know QT but assumed you would be able to affect the position in the ctor (since the parent is known there, and only there). – Ted Lyngmo Jul 11 '19 at 13:16
  • What I mean is that something like `NiceBox(const QWidget* parent = nullptr) : QMessageBox(nullptr) { if(parent) move(parent->pos()); } ` could be used to position your `QMessageBox` subclass without the need to cast. – Ted Lyngmo Jul 11 '19 at 13:28
  • @TedLyngmo You are right. Still, before messing around with window positioning, I prefer to do the `const_cast`s. – Benjamin Bihler Jul 11 '19 at 13:33
  • Yeah, that's probably simpler. – Ted Lyngmo Jul 11 '19 at 13:45

2 Answers2

9

That's not undefined. That's exactly what const_cast is for. As long as the object itself is non-const then you can cast it away with const_cast and do the same things with it as a non-const pointer.

Do note that const_cast is usually considered a code smell and might indicate bad design.


As the standard says:

In the body of a non-static ([class.mfct]) member function, the keyword this is a prvalue whose value is a pointer to the object for which the function is called. The type of this in a member function of a class X is X*. If the member function is declared const, the type of this is const X*, if the member function is declared volatile, the type of this is volatile X*, and if the member function is declared const volatile, the type of this is const volatile X*.

The type of this is const X* in your case even though the object itself is non-const.


The standard says this about const_cast:

For two similar types T1 and T2, a prvalue of type T1 may be explicitly converted to the type T2 using a const_­cast. The result of a const_­cast refers to the original entity.

So, casting from const X* to X* is also legal.


Lastly, it says (albeit in a note):

[ Note: Depending on the type of the object, a write operation through the pointer, lvalue or pointer to data member resulting from a const_­cast that casts away a const-qualifier may produce undefined behavior ([dcl.type.cv]). — end note  ]

And [dcl.type.cv] tells us:

Any attempt to modify ([expr.ass], [expr.post.incr], [expr.pre.incr]) a const object ([basic.type.qualifier]) during its lifetime ([basic.life]) results in undefined behavior.

Luckily, our this is pointing to a non-const object, so casting it and then modifying this object through the new non-const pointer doesn't trigger undefined behaviour.


Sorry Angew.

Hatted Rooster
  • 33,170
  • 5
  • 52
  • 104
  • Well, `const_cast` is one way to avoid code duplication between const and non-const member functions that do the same thing and just return stuff with different const-ness (e.g. a vector of const or non-const pointers). In fact, it's sanctioned by Scott Meyers: https://stackoverflow.com/a/123995 – Max Langhof Jul 11 '19 at 13:02
  • @MaxLanghof But Scott Meyers does it the other way round. He `const_cast`s away the constness of the return value. – Benjamin Bihler Jul 11 '19 at 13:06
  • @BenjaminBihler My remark was solely in response to _"do note that `const_cast` is usually considered a code smell and might indicate bad design"_. – Max Langhof Jul 11 '19 at 13:07
  • @MaxLanghof Very true, it's why I said might and usually, it's not always the case. – Hatted Rooster Jul 11 '19 at 13:10
  • 2
    The only smell here is of one awesome chicken. – StoryTeller - Unslander Monica Jul 11 '19 at 13:39
-1

So the point of const_cast away is to allow this type of manipulation, and you are expected to know if it is safe or not, as a programmer.

Where it could go wrong is if you have an aggregate object with "acceptable" member methods. No constructors, destructors, virtuals, mutable members, members that are themselves non-aggregates, nor may it be derived.

An instance of such an object could be declared in static space as constant, and the compiler might instance that object into a predefined immutable memory block. Performing a const_cast away on that object and trying to modify it, either with a global function or from your member function, will cause "undefined behaviour", probably an exception, possibly an uncatchable fault, almost certainly leading to program termination.

Gem Taylor
  • 4,767
  • 1
  • 5
  • 25