Need clarifications in C-style, reinterpret, and const casts
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 recomm开发者_JS百科ended 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. :)
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_cast
s 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.
No, C-style casts can act as reinterpret_cast
s, const-cast
s or static_cast
s 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
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
.
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_cast
ing 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
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.
精彩评论