22

Is the following allowed:

const int const_array[] = { 42 };

int maybe_inc(bool write, int* array) {
  if (write) array[0]++;
  return array[0];
}

int main() {
  return maybe_inc(false, const_cast<int *>(const_array));
}

In particular, is it OK to cast-away the constness of const_array, which was defined as const, as long as the object is not actually modified, as in the example?

BeeOnRope
  • 60,350
  • 16
  • 207
  • 386
  • 4
    Indeed, you are safe as long as no write is actually performed. The `const_cast` itself does not result in UB. See also (possible duplicate?): https://stackoverflow.com/questions/29883327/is-it-safe-to-remove-const-via-const-cast-and-invoke-a-non-const-function-that-d – Cody Gray - on strike Feb 03 '19 at 15:15
  • 2
    Possible duplicate of [Is it safe to remove const via const\_cast and invoke a non-const function that does not modify the resulting object?](https://stackoverflow.com/questions/29883327/is-it-safe-to-remove-const-via-const-cast-and-invoke-a-non-const-function-that-d) – Passer By Feb 05 '19 at 01:00

1 Answers1

28

Yes. This is entirely legal. (It is dangerous, but it is legal.) If you (attempt to) modify an object declared const, then the behaviour is undefined.

From n4659 (which is the last draft of C++17), section 10.1.7.1 [dcl.type.cv] para 4:

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

My emphasis. That is from C++17, but this has been true of all versions of C++.

If you look at the section on const_cast there is a note that

[ 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-qualifier76 may produce undefined behavior (10.1.7.1). — end note ]

Notes are not normative, but this strongly implies that obtaining a non-const reference or pointer to a const object is legal. It is the write that is not allowed.


New references for C++20:

From N4680 (which is the last draft of C++20), section 9.2.8.1 [dcl.type.cv] para 4:

Any attempt to modify (7.6.19, 7.6.1.5, 7.6.2.2) a const object (6.8.3) during its lifetime (6.7.3) results in undefined behavior.

If you look at the section on const_cast, section 7.6.1.10 [expr.const.cast] para 6 linking to above statement:

[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 (9.2.8.1).]

Deduplicator
  • 44,692
  • 7
  • 66
  • 118
  • 7
    Interesting, so [this answer](https://stackoverflow.com/a/19554871/149138) is wrong (or at least imprecise) when it says "You are not allowed to const_cast variables that are actually const." - the actual restriction is that you are not allowed to _modify_ variable that are actually const (and of course casting away const removes the guardrails that would normally prevent that)? – BeeOnRope Feb 03 '19 at 15:18
  • 3
  • 6
    @BeeOnRope Confirming, that quote is incorrect and your interpretation is correct. – Lightness Races in Orbit Feb 03 '19 at 15:28
  • 10
    It's actually amazing how many wrong highly upvoted answers one can find on the subject. Here's another. https://stackoverflow.com/questions/357600/is-const-cast-safe – StoryTeller - Unslander Monica Feb 03 '19 at 15:29
  • Why is this legal? Is there a limitation in the way C++ is implemented that makes it impossible to forbid this at compile time without losing important features? – Mehdi Feb 19 '23 at 17:40
  • 2
    @Mehdi It's to allow passing "actually const" objects to old APIs that were written before `const` existed in C, and haven't been updated, and for calling functions like `maybe_inc` in the original question where the programmer can see the array won't be modified, but the compiler can't. C and C++ have a long tradition of "trust the programmer". – Martin Bonner supports Monica Feb 19 '23 at 18:12
  • @MartinBonnersupportsMonica: Possibly "before `const` existed" but more likely "by teams who didn't realize how valuable const-correctness actually is" – Ben Voigt Mar 09 '23 at 17:54
  • In C++23, it's currently [(N4928)](https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2023/n4928.pdf) [dcl.type.cv] 9.2.9.2 (4) and [expr.const.cast] 7.6.1.11 (6) with the same wording. – Thomas Weller Mar 09 '23 at 18:50
  • @BenVoigt: There are many scenarios where a function may accept a pointer and pass it back to client code which may or may not modify storage identified thereby. Such functions may be correctly used either by clients that modify the storage and expect that it will be modified, or by clients that do not modify the storage and rely upon its not being modified. Const correctness would require having separate versions of the function for the two use cases, but duplicating code for such purposes would be uglier than using a const-cast to accommodate both. – supercat Mar 13 '23 at 20:45
  • @supercat: Or the library could forward callback data of type `uintptr_t` and completely avoid getting entangled with "does the client pass a pointer to const or to non-const". – Ben Voigt Mar 13 '23 at 21:45
  • @BenVoigt: Consider a collection with a function to invoke a caller-supplied callback on every item in the collection. If the callback modifies things in the collection, things in the collection will be modified. If it doesn't, they won't. I guess the collection could convert pointers to the objects to type `uintptr_t` before invoking the callback, but that would seem pretty horrible compared with having it accept a const-qualified pointer to the collection, but accept a callback which takes a non-const-qualified pointer to the element type. – supercat Mar 13 '23 at 21:57
  • @supercat: Generic (templated) collections are (a) completely different from "a function may accept a pointer and pass it back to client code and (b) going to need both const-iterator and non-const-iterator versions anyway, to respect const-ness of the collection (besides which, you can't use `const_cast` to get between const and non-const iterator flavors) – Ben Voigt Mar 13 '23 at 22:01