开发者

Need clarifications in C-style, reinterpret, and const casts

开发者 https://www.devze.com 2022-12-21 21:57 出处:网络
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

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_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.


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


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_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


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.

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号