4

In my work the use of const_cast is under some circumstances unavoidable.

Now I have to const_cast some pretty complicated types and actually I don't want to write all this type clutter in the const_cast<Clutter> expressions, especially if Clutter is very long.

My first idea was to write const_cast<>(myType), but my compiler cannot deduce the non-const type of myType. So I thought about helping my compiler and I deviced the following approach, which compiles.

#include <stdlib.h>
#include <iostream>

int main(int, char**) {
    const int constVar = 6;
    using T = typename std::remove_cv<decltype(constVar)>::type;
    auto& var = const_cast<T&>(constVar);
    var *= 2;
    std::cout << &constVar << " " << &var << "\n"; // Same address!
    std::cout << constVar << " " << var << "\n";
    return EXIT_SUCCESS;
}

Unfortunately, the program gives me the output 6 12 instead of the expected 6 6, which I really didn't understand?

What is wrong with my approach?

Aleph0
  • 5,816
  • 4
  • 29
  • 80
  • 3
    What's wrong with your approach is that it invokes undefined behavior. You're trying to modify an object that is `const`. – Michael Kenzel Dec 13 '18 at 10:11
  • @MichaelKenzel: So the use of const_cast is just to remove the const, but I'm still not allowed to modify the casted object? – Aleph0 Dec 13 '18 at 10:12
  • 2
    Yes. You can use `const_cast`, for example, to cast away the `const` on a reference that you know refers to an object that is not actually `const`. But as soon as you're attempting to modify an object that actuall is `const`, you get undefined behavior. – Michael Kenzel Dec 13 '18 at 10:14
  • @MichaelKenzel: So actually, me approach is completely sound, if I don't modify var? – Aleph0 Dec 13 '18 at 10:14
  • @MichaelKenzel: Alright. That already answers my question. Just wondering, why `auto& y=const_cast<>(constVar)`, does not compile. Would make things much easier. – Aleph0 Dec 13 '18 at 10:15
  • If you don't modify `var`, then your code is fine. Question is: If you don't need to modify `var` then why do you have to cast away the `const`? There may be some legitimate reasons to so that. But in your place, I would seriously step back and reevaluate how much truth there really is to the premise that the use of `const_cast` is unavoidable… – Michael Kenzel Dec 13 '18 at 10:16
  • @MichaelKenzel: Because a certain interface expects a non-const object, but actually it should be const. But right now I'm not able to modify the interface. – Aleph0 Dec 13 '18 at 10:17
  • 1
    C++17 added [`std::as_const()`](https://en.cppreference.com/w/cpp/utility/as_const). You could add the reverse, but it's much rarer useful, and more likely wrong. Therefore, making it easier to write, and make it look like something ordinary, is not that desirable. – Deduplicator Dec 13 '18 at 10:18
  • 2
    @Aleph0 Ok, that is one of these cases where you're forced to `const_cast`. However, whenever a library interface is forcing your hand, you can just write some inline helper functions that wrap the respective library calls and perform the cast. That should limit the number of places where you need to cast to just a few and also allows you to quickly remove the casts as soon as the library catches up with the 21st century… – Michael Kenzel Dec 13 '18 at 10:25
  • @MichaelKenzel: Thanks for your hint. – Aleph0 Dec 13 '18 at 10:27
  • The Q is ambiguous: how many const do you wish to remove? What do you expect here `auto_const_cast<>((const int* const*)0)`? – curiousguy Feb 20 '19 at 20:39

2 Answers2

6

From the documentation of const_cast:

const_cast makes it possible to form a reference or pointer to non-const type that is actually referring to a const object or a reference or pointer to non-volatile type that is actually referring to a volatile object. Modifying a const object through a non-const access path and referring to a volatile object through a non-volatile glvalue results in undefined behavior.

So what you have is undefined behavior.

Also of interest is this note from cv type qualifiers.

const object - an object whose type is const-qualified, or a non-mutable subobject of a const object. Such object cannot be modified: attempt to do so directly is a compile-time error, and attempt to do so indirectly (e.g., by modifying the const object through a reference or pointer to non-const type) results in undefined behavior.

P.W
  • 26,289
  • 6
  • 39
  • 76
2

If you have

void foo(const int& a)
{
    const_cast<int&>(a) = 4;
}

then

int a = 1;
foo(a);

is perfectly legal, but

const int a = 1;
foo(a);

invokes an undefined behaviour, because in foo, a was originally const.

This is useful in some case (usually when interfacing old C library), but in most cases, you are doing something wrong and should rethink your solution.

And to answer why const_cast<> isn't a thing, I'd say for two reasons. First, when you do const_cast you should really know what you are doing, if some kind of template deduction was allowed, it would make doing unintended mistakes more likely to occur. And secondly const_cast can also be used to remove volatile and how can compiler know what you want to cast away?

Zereges
  • 5,139
  • 1
  • 25
  • 49