11

Recently I found a piece of C++ code that effectively does the following:

char* pointer = ...;
const char* constPointer = const_cast<const char*>( pointer );

Obviously the author thought that const_cast means "add const", but in fact const can be just as well added implicitly:

const char* constPointer = pointer;

Is there any case when I would really have to const_cast to a pointer-to-const (const_cast<const Type*> as in above example)?

sharptooth
  • 167,383
  • 100
  • 513
  • 979
  • 3
    Only one I can think of is to force a specific type for template instantiation - or for overload resolution. – Erik Mar 16 '11 at 10:56
  • 2
    One interesting wrinkle, though I can't see why that would make the construct any more useful, is that you can use `const_cast` to add or subtract `volatile` as well as `const`. – Omnifarious Mar 16 '11 at 11:01
  • 1
    I also wonder how it differs from static_cast – StackedCrooked Mar 16 '11 at 12:00
  • 1
    @Omnifarious: indeed, and that's why *not* to use `const_cast` to add `const`. The whole point of the restricted conversions in C++ is so you can use the one which does as little as possible including what you want. I wonder whether it would be useful to write an `implict_cast` template, that allows you to signal in code that a conversion is taking place, but ensure that only implicit conversions are performed. Therefore you won't accidentally remove `volatile` (as with `const_cast` to a pointer type) or use an explicit constructor (as with `static_cast` to a class type). – Steve Jessop Mar 16 '11 at 12:15
  • 3
    @StackedCrooked: when the target is `const char*`, I think the only difference is that `const_cast` can remove `volatile` and `static_cast` can't. For pointers to class types, though, `const_cast` won't upcast or (even more crucially) downcast and `static_cast` will. Basically, they have different ways of accidentally doing something you didn't mean when the source type isn't quite what you thought it was. – Steve Jessop Mar 16 '11 at 12:21
  • This discussion makes me wonder, wouldn't the following be a better way to explicitly add constness: `template const T& const_ref(const T& t) { return t; }`? This wouldn't touch volatileness, nor try to cast the object to a different type altogether (as long as you let template type deduction do it's work normally). – UncleBens Mar 16 '11 at 17:15

4 Answers4

11

Where you have 2 overloads and you want to force the const one to be executed. This is often the case when you call one in terms of the other.

class A
{
public:
   B* get();
   const B* get() const;
};

I have a non-const A but want to run get() const I might cast. In particular I might do this in the implementation of the non-const itself.

B* A::get() 
{
   return const_cast<B*>( const_cast< const A*>(this)->get() );
}

Of course we could do:

B* A::get()
{
    const A* constthis = this; // no need to cast
    return const_cast<B*>(constthis->get());
}

so we did not have to cast but it makes the first solution a one-liner and no need to create a temp variable.

CashCow
  • 30,981
  • 5
  • 61
  • 92
  • You can avoid typing one `const_cast<>` by doing the reverse: implementing a const member function in terms of a non-const one. This is because conversion `T*` to `T const*` does not require an explicit cast. – Maxim Egorushkin Mar 16 '11 at 14:56
  • 2
    @MaximYegorushkin: That will open the door to [undefined behaviour land](http://stackoverflow.com/a/123765/1969455)! Sorry for the late comment, but I think this warning is important. – Brandlingo Oct 09 '14 at 08:59
  • @MatthäusBrandl When an object is non-const, casting it to const and back is well defined. – Maxim Egorushkin Oct 09 '14 at 09:06
  • Maybe I read your comment wrong? Implementing the `const` function in terms of the non-`const` one sounds like casting away this' constness to call the non-const member and then cast the result back to const. – Brandlingo Oct 09 '14 at 11:29
  • http://stackoverflow.com/questions/123758/how-do-i-remove-code-duplication-between-similar-const-and-non-const-member-func – Ciro Santilli OurBigBook.com Dec 22 '16 at 01:31
6

const_cast, despite its name, is not specific to const; it works with cv-qualifiers which effectively comprises both const and volatile.

While adding such a qualifier is allowed transparently, removing any requires a const_cast.

Therefore, in the example you give:

char* p = /**/;
char const* q = const_cast<char const*>(p);

the presence of the const_cast is spurious (I personally think it obscures the syntax).

But you can wish to remove volatile, in which case you'll need it:

char const volatile* p = /**/;
char const* q = const_cast<char const*>(p);

This could appear, for example, in driver code.

Matthieu M.
  • 287,565
  • 48
  • 449
  • 722
5

Maybe to force overload resolution in cases where you have f(T*) and f(const T*).

zvrba
  • 24,186
  • 3
  • 55
  • 65
  • Though if those two functions do something distinct their author should probably be slapped a few times. – edA-qa mort-ora-y Mar 16 '11 at 12:40
  • @edA-qa mort-ora-y: Does copying the input to a temporary data object count as something distinct? – David Thornley Mar 16 '11 at 13:51
  • 1
    By distinct I mean the return/effect of the function. It's perfectly reasonably to implement them differently, but the two forms should have the same ultimate result. That is, it shouldn't matter which one is called, the high-level meaning of the program should not change. – edA-qa mort-ora-y Mar 16 '11 at 14:01
3

You can use static_cast to add const as well. So I don't see any situation where you have to use const_cast to add const. But explicit casting (be it one or another) can sometimes be needed when you want to change the type of the object for example for overload resolution.

E.g.

void f(char*);
void f(const char*);

int main()
{
   char* p = 0;
   f(p); //calls f(char*)
   f(static_cast<const char*>(p)); //calls f(const char*);
   f(const_cast<const char*>(p)); //calls f(const char*);
}
Armen Tsirunyan
  • 130,161
  • 59
  • 324
  • 434
  • 3
    `statc_cast` is dangerous to use if all you want to do is add `const`. – Omnifarious Mar 16 '11 at 10:59
  • 2
    @Armen Tsirunyan: Because if someone changes the type of the variable you're casting, the cast might well cast it to some random and unrelated type for no apparent reason. It makes the code harder to maintain. – Omnifarious Mar 16 '11 at 11:03
  • @Omnifarious: I am sorry but that really doesn't make any sense. By the same logic the type could have changed to add a second const, and the const_cast would remove it... – Armen Tsirunyan Mar 16 '11 at 11:04
  • 1
    Suppose, when you first write your code, the variable is of type `Derived *`. And you `static_cast(v)`. Then, later on, someone changes the type of the variable to `Base *`. Oops, the `static_cast` still blindly does the cast whether or not it's valid. A `const_cast` would cause a compiler error. – Omnifarious Mar 16 '11 at 11:07
  • @Omnifarious: OK, you somewhat convinced me :) – Armen Tsirunyan Mar 16 '11 at 11:08
  • @Armen Tsirunyan: You are correct, that could happen. But that's a more specific kind of error than if you had `static_cast`. The number of possible types you could change the variable to be that would cause a problem is much greater with `static_cast` than `const_cast`. – Omnifarious Mar 16 '11 at 11:09
  • @Omnifarious: Why don't you sum up your idea in a good answer which I would gladly upvote? – Armen Tsirunyan Mar 16 '11 at 11:12
  • 1
    @Armen: No, I agree with Omnifarious. It's plain to see that explicitly writing out the variable's base type again is creating an unnecessary potential point of failure. – Lightness Races in Orbit Mar 16 '11 at 11:13
  • @Tomalak: As you can see, I agree with him too – Armen Tsirunyan Mar 16 '11 at 11:15
  • @Armen: Your conversation unrolled too fast for me :P – Lightness Races in Orbit Mar 16 '11 at 11:20