11

I know that casting away const-ness should be done with care, and any attempt to remove const-ness from an initially const object followed by modifying the object results in undefined behaviour. What if we want to remove const-ness so that we may invoke a non-const function which doesn't modify the object? I know that we should actually mark such function const, but suppose I'm using a "bad" code that doesn't have the const version available.

So, to summarize, is the code below "safe"? My guess is that as long as you don't end up modifying the object you're ok, but I'm not 100% sure.

#include <iostream>

struct Foo
{
    void f() // doesn't modify the instance, although is not marked const
    {
        std::cout << "Foo::f()" << std::endl;
    }
};

int main()
{
    const Foo foo;
    const_cast<Foo&>(foo).f(); // is this safe?
}
vsoftco
  • 55,410
  • 12
  • 139
  • 252
  • 1
    Regardless of the fact that it's allowed by the standard, as answered already, I would say it's neither safe nor okay. –  Apr 26 '15 at 21:03
  • @hvd Why would a read-only operation be unsafe? – Barry Apr 26 '15 at 21:10
  • 2
    @Barry That's not the point. That member function can be modified later, to accidentally invoke UB. – Columbo Apr 26 '15 at 21:11
  • @hvd I've seen code like this in some proxy class implementation, where the ctor was taking a non-const param. The usage came in a function that was returning a const proxy, but had to invoke the non-const ctor to construct the proxy. Quite obfuscated, but nevertheless real code. And yes, I know one shouldn't write such code, I was just curios if technically it is ok. – vsoftco Apr 26 '15 at 21:11
  • Does this answer your question? [Is it allowed to cast away const on a const-defined object as long as it is not actually modified?](https://stackoverflow.com/questions/54504247/is-it-allowed-to-cast-away-const-on-a-const-defined-object-as-long-as-it-is-not) – user202729 Jan 19 '22 at 16:10

3 Answers3

9

Undefined behavior with respect to const_cast is defined by the C++11 standard's §3.8/9 (§3.8 is “Object lifetime”):

Creating a new object at the storage location that a const object with static, thread, or automatic storage duration occupies or, at the storage location that such a const object used to occupy before its lifetime ended results in undefined behavior.

and §7.1.6.1/4 (§7.1.6.1 is “The cv-qualifiers”)

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

In other words, you get UB if you modify an originally const object, and otherwise 1not.

The const_cast itself doesn't introduce UB.


There is additionally a non-normative note at §5.2.11/7 that “depending on the type” a write through the pointer or reference obtained from a const_cast, may have undefined behavior.

This non-normative note is so wooly that it has its own non-normative footnote, that explains that “const_cast is not limited to conversions that cast away a const-qualifier.”.

However, still with that clarification I fail to think of any case where the write could be well-defined or not depending on the type, i.e., I fail to make sense of this note. Two other answers here focus on the word “write” in this note, and that's necessary to get into UB-land via §3.8/9, yes. The to me rather fishy aspect is the “depending on the type”, which appears to be the significant part of that note.


1) Except insofar as UB-rules about other non-const_cast-related things get into play, e.g. nulling a pointer that's later dereferenced in a context other than as typeid-expression.

Cheers and hth. - Alf
  • 142,714
  • 15
  • 209
  • 331
  • 1
    There's also: 7.1.6.1 "Except that any class member declared mutable (7.1.1) can be modified, any attempt to modify a const object during its lifetime (3.8) results in undefined behavior." – CB Bailey Apr 26 '15 at 21:22
  • "Depending on the type" makes sense if it refers to the type used when defining the object. A `const`-qualifier used in the definition is part of the type, and whether modifications are allowed depends on whether that qualifier was used. –  Apr 26 '15 at 21:37
  • @hvd: Thanks, that makes sense. A kind of twisted sense since they could just have written that. Still, I think that's a likely explanation. :) – Cheers and hth. - Alf Apr 26 '15 at 21:38
7

This particular example happens to be safe (have well defined behavior) because there is no write to an object which is declared const.

CB Bailey
  • 755,051
  • 104
  • 632
  • 656
6

We have this in [dcl.type.cv]:

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

And there is a note (non-normative) in [expr.const.cast] which states:

[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 (7.1.6.1). —end note ]

An attempt to modify an object or a write operation after a const_cast [may] result in undefined behavior. Here, we have no write operation.

Barry
  • 286,269
  • 29
  • 621
  • 977