views:

148

answers:

5

Am I right in assuming that C-style casts (which are discouraged) are nothing but reinterpret_casts? Using the latter is visually striking and easy to search when looking for nasty casts, and hence it's recommended over C-style casts?

If casting away const using const_cast and writing to a originally const object is undefined, what is the purpose of const_cast?

Note: I know that Bjarne rightly condemns casting operations that they are unsafe and even goes to the extent of stating "An ugly operation should have an ugly syntactic form." and hence the verbosity of casting operators in C++. So I'll try to minimize their usage. Promise. :)

A: 

const_cast is used to remove const from a type. It also can remove volatile. If the object really is const then the result cannot be written to and still be well-defined behavior. If, however, it is promoted to const (by being passed into a const T function, then const_casting it back to non-const is ok. ( i found some more info here)

reinterpret_cast cannot remove const or volatile from a type.

see also

KitsuneYMG
One more use for const_cast: If a function doesn't obey const correctness, you need to use a const_cast to pass in a const even if the function doesn't modify the parameter.
Swiss
+1  A: 

No, C-style casts can act as reinterpret_casts, const-casts or static_casts depending on the situation. This is why they are discouraged - you see a C-style cast in code and need to look for details to see what it will do. For example:

const char* source;
int* target = (int*)source;// - acts as const_cast and reinterpret_cast at once
//int* target = retinterpret_cast<int*>source;// - won't compile - can't remove const
sharptooth
+1 for showing that `reinterpret_cast` cannot be used in place of `const_cast` while C-style casts can be.
legends2k
A: 

C-style casts are really the sledge hammer of programming - you basically tell the compiler that the square peg over there will fit through this round hole no matter what. In that sense, reinterpret_cast is very similar.

The main advantage I see in using the C++-style cast operators are that they allow you to express your intent better and allow the compiler to still to some checking on the operation you're asking it to perform rather than the one-size-fits-all style C cast.

Regarding const_cast- you often get into the situation where you are passing an object around via const reference simply because the API requires you to do this. Say, you've got function X that tasks a C-style string:

void X(const char *str) { ... }

Inside that function you're passing the parameter to a C function that expects a char *, even though it's not changing the string. The only way to accommodate this would be to const_cast str.

I'd be very careful using any sort of cast, often this shows that there is something not quite right with your design but sometimes you have to convince the compiler that the peg it's looking at isn't as square as it assumes. Only then should you use the cast operators.

Timo Geusch
+3  A: 

No. A C cast can do the equivalent of a const_cast, a static_cast, a reinterpret_cast, or a combination thereof. In case that wasn't quite enough, it can also do at least one minor trick that no combination of the newer casts can do at all!

You can use const_cast with defined results if the original variable is defined without const, but all you have is a const pointer or reference to that object. OTOH, if you think you have a good reason to use a const_cast, chances are that you should really look up mutable instead.

Edit: I suppose I should have said it right off, but a C-style cast can convert to an an inaccessible base class. For example, consider something like:

[Edit: I'm updating the code to something that'll compile and (usually) demonstrate problem. ]

#include <iostream>

class base1 {
public:
    virtual void print() { std::cout << "base 1\n"; }
};

class base2 {
public:
   virtual void print() { std::cout << "base 2\n"; }
};

class derived : base1, base2 {}; // note: private inheritance

int main() {    
    derived *d = new derived;
    base1 *b1 = (base1 *)d;    // allowed
    b1->print();    // prints "base 1"
    base2 *b2 = (base2 *)d;    // also allowed
    b2->print();    // prints "base 2"

//    base1 *bb1 = static_cast<base *>(d);  // not allowed: base is inaccessible

    // Using `reinterpret_cast` allows the code to compile.
    // Unfortunately the result is different, and normally won't work. 
    base1 *bb2 = reinterpret_cast<base1 *>(d);
    bb2->print();   // may cause nasal demons.

    base2 *bb3 = reinterpret_cast<base2 *>(d); 
    bb3->print();   // likewise
    return 0;
}

The code using the reinterpret_casts will compile -- but attempting to use the result (of at lest one of the two) will cause a major problem. The reinterpret_cast takes the base address of the derived object and attempts to treat it as if it was the specified type of base object -- and since (at most) one base object can actually exist at that address, trying to treat it as the other can/will cause major problems. Edit: In this case, the classes are essentially identical except for what they print, so although anything could happen, with most compilers, both of the last two will print out "base 1". The reinterpret_cast takes whatever happens to be at that address and tries to use it as the specified type. In this case, I've (tried to) make that do something harmless but visible. In real code, the result probably won't be so pretty.

The C-style cast will work like a static_cast would if the code had used public inheritance instead of private -- i.e. it's aware of where in the derived class each base class object "lives", and adjusts the result, so each resulting pointer will work because it's been adjusted to point at the right place.

Jerry Coffin
What is this minor trick that a C-style cast can do?
R Samuel Klatchko
@Jerry: +1 to Samuel. May I know what it is?
legends2k
Thanks for all the answers, but Jerry's example made it clear! Also I guess I can take it that `const_cast` can only be used to add const but never to cast away const (if the original is a const) and if one shouldn't hit undefined behaviour.
legends2k
@legends2k: const_cast should only be used to take away const. You don't need it to add const. If you think you shouldn't be taking away const, then don't use const_cast at all.
Steve Jessop
@Steve: I know that one can assign a non-const object to a const param/id without a cast, but to cast away the `const` for any original const shouldn't be done is what I meant. So I can use it to cast away cast only if I'm sure that it's a `const` by birth.
legends2k
For those curious about the "minor trick", it's specified in the standard section 5.4, paragraph 7. I didn't know about it and just looked it up.
jalf
+1  A: 

Remember, that a const cast may be acting on something other then the original identifier:

void doit(const std::string &cs)
{
    std::string &ms = const_cast<std::string &>(cs);
}

int main()
{
    std::string s;
    doit(s);
}

So while doit is casting away const, in this example the underlying string is not const so no undefined behavior.

Update

Okay, here's a better example of when using const_cast is not completely worthless. We start with a base class with a virtual function that takes a const parameter:

class base
{
public:
    virtual void doit(const std::string &str);
};

and now you want to override that virtual function.

class child : public base
{
public:
    virtual void doit(const std::string &str)
    {
        std::string &mstr = const_cast<std::string &>(str);
    }
};

Because of the logic/structure of your code, you know that child::doit will only be called with non-const strings (and class base is not under your control so you can't modify it nor can you change the signature of child::doit because then it will not longer override base::doit). In this case, it's safe to cast away const.

Yes, this is risky. Perhaps when you write that, it's true that the execution will never reach child::doit with a non-const string and the code is valid. But that could change either while maintaining your program or perhaps when you rebuild and pick up the latest version of class base.

R Samuel Klatchko
I get it, but why get it as a const and then cast it away, instead `doit`'s param can be a non-const, can't it?
legends2k
@legends2k:It can, but that won't let you pass a temporary object.
Jerry Coffin
@Jerry: Oh right, temporaries by the compiler are always const coz they're rvalues. But tell me this, if I write like Samuel's shown up there, where the function protocol says the argument is a `const` while in the implementation if I use it like a non-const (by casting away the const using `const_cast`, how will the client programmer know that this function will actually modify the param in-place? If she accidentally passes a non-const original to the function, it enters undefined behaviour.
legends2k
@legends2k:no, it enters has undefined behavior only if what was originally passed *was* const. As far as how to be sure when it's safe and when it's not, that's a large part of why I said that when you think you want `const_cast`, you usually really want `mutable`.
Jerry Coffin
@Jerry: I actually wanted to ask, "if in the header/function protocol I define a function's param as `const` and inside the function casting away the `const` like Samuel has shown, is incorrect, right? Since the client user will assume that the function won't modify the value and pass a `const` which will actually cast it away and do a write, which is undefined. So even for passing a temporary object's sake one shouldn't define the param `const` *if* the function is going to modify it." I didn't write it correctly in my prev. comment, sorry.
legends2k
@legends2k - I updated my answer to give a slightly better example of when `const_cast` may not be completely silly.
R Samuel Klatchko
@legends2k:As I keep saying, yes, real uses for const_cast are fairly unusual. Doing a quick grep across some of my code, it looks like most times I've used it were to pass data through to functions that weren't const-correct. I.e. they didn't modify their parameter, but also didn't mark it as const, so I had to cast away the const to call them with it.
Jerry Coffin
@Jerry/@Samuel: Thanks to you guys for taking the time to explain my every cross question; I can understand the implications now; Like all casts, avoiding it altogether when possible and using it only when one has to (when there's no other go) will do.
legends2k