Should I Use a C++ Reinterpret_Cast Over a C-Style Cast

Should I use a C++ reinterpret_cast over a C-style cast?

The problem with C-Style casts is that they do a lot under the hood. See here for a detailed explanation: http://anteru.net/2007/12/18/200/

You should try to always use the C++-casts, makes life easier in the long run. The main problem with C-style casts in this case is that you could have written (char*)(&v) while with reinterpret_cast, you would need an additional const_cast, so it's a bit safer. Plus you can easily find reinterpret_cast with a regex, which is not possible for the C-style casts.

When should static_cast, dynamic_cast, const_cast, and reinterpret_cast be used?

static_cast is the first cast you should attempt to use. It does things like implicit conversions between types (such as int to float, or pointer to void*), and it can also call explicit conversion functions (or implicit ones). In many cases, explicitly stating static_cast isn't necessary, but it's important to note that the T(something) syntax is equivalent to (T)something and should be avoided (more on that later). A T(something, something_else) is safe, however, and guaranteed to call the constructor.

static_cast can also cast through inheritance hierarchies. It is unnecessary when casting upwards (towards a base class), but when casting downwards it can be used as long as it doesn't cast through virtual inheritance. It does not do checking, however, and it is undefined behavior to static_cast down a hierarchy to a type that isn't actually the type of the object.


const_cast can be used to remove or add const to a variable; no other C++ cast is capable of removing it (not even reinterpret_cast). It is important to note that modifying a formerly const value is only undefined if the original variable is const; if you use it to take the const off a reference to something that wasn't declared with const, it is safe. This can be useful when overloading member functions based on const, for instance. It can also be used to add const to an object, such as to call a member function overload.

const_cast also works similarly on volatile, though that's less common.


dynamic_cast is exclusively used for handling polymorphism. You can cast a pointer or reference to any polymorphic type to any other class type (a polymorphic type has at least one virtual function, declared or inherited). You can use it for more than just casting downwards – you can cast sideways or even up another chain. The dynamic_cast will seek out the desired object and return it if possible. If it can't, it will return nullptr in the case of a pointer, or throw std::bad_cast in the case of a reference.

dynamic_cast has some limitations, though. It doesn't work if there are multiple objects of the same type in the inheritance hierarchy (the so-called 'dreaded diamond') and you aren't using virtual inheritance. It also can only go through public inheritance - it will always fail to travel through protected or private inheritance. This is rarely an issue, however, as such forms of inheritance are rare.


reinterpret_cast is the most dangerous cast, and should be used very sparingly. It turns one type directly into another — such as casting the value from one pointer to another, or storing a pointer in an int, or all sorts of other nasty things. Largely, the only guarantee you get with reinterpret_cast is that normally if you cast the result back to the original type, you will get the exact same value (but not if the intermediate type is smaller than the original type). There are a number of conversions that reinterpret_cast cannot do, too. It's used primarily for particularly weird conversions and bit manipulations, like turning a raw data stream into actual data, or storing data in the low bits of a pointer to aligned data.


C-style cast and function-style cast are casts using (type)object or type(object), respectively, and are functionally equivalent. They are defined as the first of the following which succeeds:

  • const_cast
  • static_cast (though ignoring access restrictions)
  • static_cast (see above), then const_cast
  • reinterpret_cast
  • reinterpret_cast, then const_cast

It can therefore be used as a replacement for other casts in some instances, but can be extremely dangerous because of the ability to devolve into a reinterpret_cast, and the latter should be preferred when explicit casting is needed, unless you are sure static_cast will succeed or reinterpret_cast will fail. Even then, consider the longer, more explicit option.

C-style casts also ignore access control when performing a static_cast, which means that they have the ability to perform an operation that no other cast can. This is mostly a kludge, though, and in my mind is just another reason to avoid C-style casts.

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

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.

When to use reinterpret_cast without disobeying the strict aliasing rule?

When you use a reinterpret_cast in your code, your are telling the compiler: "I know what I'm doing – just implement the cast and trust me that the result will be OK to use." The compiler will then use the result of that cast as it would any other object of the specified destination type.

So, if you know that a particular value is the address of an actual float data type, then you can safely cast (say) an intptr_t value to a float* and dereference the resulting pointer.

A common case for such use of the reinterprt_cast occurs in Windows (WinAPI) programming: the LPARAM of a Windows message is often used to point to a particular type of data structure. Here is a 'pseudo-example' of a handler for the WM_NOTIFY message:

BOOL OnNotify(WPARAM wParam, LPARAM lParam, LRESULT *pResult)
{
NMHDR *pHdr = reinterpret_cast<NMHDR *>(lParam);
switch (pHdr->code) {
// ... do some stuff with the data in the referenced NMHDR structure
}
//...
*pResult = 0;
return TRUE;
}

In this case, the Windows framework has 'promised' that the lParam argument received will be the address of a NMHDR structure, so the cast is safe. (Note that other C++ casts won't work for such a conversion: only reinterpret_cast or a "C-Style" cast can convert the integer type to a pointer.)


However, using any sort of cast (reinterpret_cast or C-style, especially) for so-called type punning is never a good idea, because of the strict aliasing rules you have mentioned in your question. To copy the bits ("as-is") from an int to a float (assuming those types are the same size, and that you don't have access to the C++20 std::bit_cast), you should use std::memcpy, instead of dereferencing aliased pointers.

Regular cast vs. static_cast vs. dynamic_cast

static_cast

static_cast is used for cases where you basically want to reverse an implicit conversion, with a few restrictions and additions. static_cast performs no runtime checks. This should be used if you know that you refer to an object of a specific type, and thus a check would be unnecessary. Example:

void func(void *data) {
// Conversion from MyClass* -> void* is implicit
MyClass *c = static_cast<MyClass*>(data);
...
}

int main() {
MyClass c;
start_thread(&func, &c) // func(&c) will be called
.join();
}

In this example, you know that you passed a MyClass object, and thus there isn't any need for a runtime check to ensure this.

dynamic_cast

dynamic_cast is useful when you don't know what the dynamic type of the object is. It returns a null pointer if the object referred to doesn't contain the type casted to as a base class (when you cast to a reference, a bad_cast exception is thrown in that case).

if (JumpStm *j = dynamic_cast<JumpStm*>(&stm)) {
...
} else if (ExprStm *e = dynamic_cast<ExprStm*>(&stm)) {
...
}

You can not use dynamic_cast for downcast (casting to a derived class) if the argument type is not polymorphic. For example, the following code is not valid, because Base doesn't contain any virtual function:

struct Base { };
struct Derived : Base { };
int main() {
Derived d; Base *b = &d;
dynamic_cast<Derived*>(b); // Invalid
}

An "up-cast" (cast to the base class) is always valid with both static_cast and dynamic_cast, and also without any cast, as an "up-cast" is an implicit conversion (assuming the base class is accessible, i.e. it's a public inheritance).

Regular Cast

These casts are also called C-style cast. A C-style cast is basically identical to trying out a range of sequences of C++ casts, and taking the first C++ cast that works, without ever considering dynamic_cast. Needless to say, this is much more powerful as it combines all of const_cast, static_cast and reinterpret_cast, but it's also unsafe, because it does not use dynamic_cast.

In addition, C-style casts not only allow you to do this, but they also allow you to safely cast to a private base-class, while the "equivalent" static_cast sequence would give you a compile-time error for that.

Some people prefer C-style casts because of their brevity. I use them for numeric casts only, and use the appropriate C++ casts when user defined types are involved, as they provide stricter checking.

What is the difference between static_cast and C style casting?

C++ style casts are checked by the compiler. C style casts aren't and can fail at runtime.

Also, c++ style casts can be searched for easily, whereas it's really hard to search for c style casts.

Another big benefit is that the 4 different C++ style casts express the intent of the programmer more clearly.

When writing C++ I'd pretty much always use the C++ ones over the the C style.

What does a C cast really do?

A cast in C is only meaningful at compile time because it tells the compiler how you want to manipulate a piece of data. It does not change the actual value of the data. For example, (int*)p tells the compiler to treat p as a memory address to an integer. However this costs nothing at run time, the processor just deals with raw numbers the way they are given to it.

Is it always safe to change a C-style cast to a static_cast?

C-style cast is generally a combo of static_cast<> or reinterpret_cast<> with const_cast<>. There might be compile-time errors from replacing it with just static_cast<>.

I am not aware of any cases for runtime errors.

You could start with static_cast<>, then add (or replace it with) const_cast<> where compile-time errors arise. If after that you still have compile-time errors, reinterpret_cast<> are required. But do not make the replacement blindly - some of these cases may be bugs. E.g., reinterpret_cast<> may be required if a data type is forward declared but not defined, which leads to unspecified behavior (and will definitely cause problems if multiple inheritance is involved).

Finding these kinds of bugs is the reason why this exercise improves safety of the source code, and why a static code analyzer flags C-style casts.

In C++, can we use { } for C-Style casting?

Is int{c} another way of casting data types?

Yes. T{value} creates a temporary of type T that is direct-list-initialized with the specified braced-init-list. This cast does have an advantage over T(value) in that T{value} can be used to create a temporary array. That would be done like

int main() {
using int_array = int[5];
for( auto e : int_array{1,2,3,4,5})
std::cout << e;
}

It also comes with the caveat that a narrowing conversion is a error

int main() {
int(10000000000ll); // warning only, still compiles
int{10000000000ll}; // hard error mandated by the standard
}

After some research on the net, I know that C++ casting is different and it have the compiler check the casting possibility at the compile time, but what are the differences between 1 and 2?

The big difference between T(value) and (T)value is that in T(value), T must be a single word. For example

int main() {
unsigned int(10000000); // error
(unsigned int)10000000; // compiles
}

Q3: Are there any other ways to explicitly convert/cast?

Well in C++ they want you to use the C++ casts which are static_cast, reinterpret_cast, dynamic_cast, and const_cast. Those are preferred over c style cast as a c style cast will do all of those where the C++ versions have certain limitations and come with certain guarantees.



Related Topics



Leave a reply



Submit