r/cpp_questions • u/le_disappointment • 18h ago
OPEN Is there any reason to use std::reinterpret_cast?
I know that std:: reinterpret_cast is used by people to cast the bits of an object of type T1 to an object of type T2, while retaining the exact bits. However, as far as I know this triggers UB. Before C++20 and std::bit_cast, we could std::memcpy the objects to avoid UB, and after C++20, we can use std::bit_cast. Given this, I don't see any legitimate reason for why one might need std::reinterpret_cast. If so, why hasn't this feature been deprecated yet?
15
u/flyingron 18h ago
It's not necessarily UB, but it certainly can be used to do so.
For instance, the following are well-defined:
int my_int;
intptr_t ptr_in_int = reinterpret_cast<intptr_t>(&my_int);
int* my_ptr = reinrepret_cast<int*>(ptr_in_int);
*my_ptr = 5;
23
u/RazzmatazzLatter8345 18h ago
It's reinterpret_cast ... built in, no std:: required.
reinterpret_cast is useful for interpreting a large object as a byte array without need for copying. It's also pretty good at casting stuff to and from a void* if you're writing some kind of type-erasure based artifact.
It's true that reinterpret_cast is hard to use correctly and that a lot of use cases are better handled by std::bit_cast or std::memcpy, but it's hard to imagine how one would cast a void* back to what it actually is without it. So reinterpret_cast is a rarely-to-be-used but essential mechanism.
8
u/not_some_username 18h ago
You can static_cast a void* to any type
12
u/LokiAstaris 15h ago
But using
reinterpret_cast<void*>()is a marker that conveys information. It is telling the next engineer that there are dragons here and to tread carefully. There are only a limited number of valid actions after this.The use of
static_cast<>()indicates that this is a safer cast and that subsequent engineers may apply less scrutiny to it.7
u/I__Know__Stuff 13h ago
No, you can static cast a void * to/from any pointer type.
I frequently need to treat a void * as a uintptr_t when setting up page tables, writing to device registers, etc.
5
u/etaithespeedcuber 17h ago
Say you wanted to load and run a DLL function, for example on windows:
cpp
auto address = GetProcAddress(...);
auto function = reinterpret_cast<void(*)(int)>(address);
function(1);
Since windows doesn't know the parameters and return type of your function, it can't return specifically that function pointer type in a way that's resolved at compile time. Therefore, you need to reinterpret whatever it returns as that function pointer type.
13
u/kevinossia 18h ago
You can use it to convert pointers to integers and inspect the byte representation of any object. Neither of those use cases are UB.
It’s also needed for APIs like write() where the input has to be a char*.
For the most commonly thought of use cases (object serialization) yes it is UB and you need to just use memcpy and/or bitcast.
4
u/the_poope 17h ago
Many C libraries define their own simple data structs and you have to pass a pointer to your data (often a large array of data) in your own type.
A example is e.g. the widely used BLAS and LAPACK library APIs. For instance Intel's C interface of LAPACKE_zgetrf takes a pointer to a matrix of type lapack_complex_double. In your C++ program you'd probably use std::complex<double>. And no, you're not gonna memcpy a 20 GB matrix. The two types lapack_complex_double and std::complex<double> are guaranteed to be two double precision floating point numbers after each other with no padding in between. So yes, it's UB, but it works and there is no way around using reinterpret_cast or C style casts.
6
u/AKostur 18h ago
bit_cast imposes a copy, reinterpret_cast does not. And is still useful to do things that the language won’t let you, but every implementation does define the result of, like taking a pointer to an array of char, and casting that to a pointer to struct representing a network packet (for example). Or casting between suitably-compatible structs.
Though it is a place where you are telling the compiler to stop checking things and to trust the programmer.
0
u/TehBens 18h ago
Or casting between suitably-compatible structs.
The (correct) point that OP makes is that this is will quite often result in UB because of the aliasing rules. char, unsigned char and std::byte are the only exceptions to this.
bit_cast imposes a copy, reinterpret_cast does not.
Both will create the same assembly as long as you don't disable all optimizations.
3
u/alfps 15h ago
❞ Both will create the same assembly as long as you don't disable all optimizations.
Logically impossible.
Consider a call
foo( reinterpret_cast<const double*>( p_bytes ) ). You don't know which partfoowill use, and neither does the compiler. Copying the zillion bytes for abit_castthen has an overhead of roughly a zillion, and can not be the "same assembly".
3
u/Independent_Art_6676 17h ago
what SHOULD happen is the RI cast should do what memcpy and bit cast do. Then we would have a properly named tool to do the job that it says it is doing, and not two other tools that do something completely different from what their names stay is happening. Its a clusterfuck that probably has some eggheadery that makes sense to the committee or they put something in the punch that day, but the result is utter crap.
So what SHOULD happen is RI cast should work properly (it does not, currently), memcpy should copy memory as it always did, and bit-cast should get the axe.
0
u/Big-Rub9545 17h ago
What does RI cast do improperly? And memcpy already does that.
2
u/Independent_Art_6676 17h ago
It does not correctly type pun some cases: many uses of the cast will result in UB whereas memcpy or bit cast will not, for the same need.
Yes, memcpy already does that, but now it is being thrown into code all over where no copying is done. Its confusing to read, its like having your + operator do xor and your - operator print text for a class.
0
u/Big-Rub9545 16h ago
I’m not sure which exact cases you’re referring to, but I will say that many applications of type punning are UB with or without RI. Using memcpy or bit_cast just circumvents the issue without performing any direct type punning.
As an adjacent example, accessing a union member other than the one last written to is UB (invalid type punning), but accessing the correct one then performing a cast is completely valid (assuming the cast itself is so). The latter isn’t really type punning, though, at least not directly.
1
u/Independent_Art_6676 12h ago
Ill keep it simple. Say on your system its faster to absolute value by hand than with the built in function by converting your float or double to integer and clearing the offending sign bit. reinterpret cast does not allow this simple thing.
3
u/YoshiDzn 16h ago
Yes, it's absolutely a savior in type erasure strategies. Think of delivering type erased functions and their payloads to a queue for later processing on worker threads. (A global injector)
This isn't something you'll cleanly educate yourself with in a reddit thread. Take a look at what a "thunk" is or how to "trampoline" work from a queue to a thread process.
2
u/No-Dentist-1645 18h ago
You can't legally do this in C++, but it will work on every architecture you are probably targeting and is sometimes necessary to make APIs with different char types talk to each other:
const char* cstring = "Hello, World";
const char8_t* unicode_string = reinterpret_cast<const char8_t*>(cstring);
// or vice versa, too
2
2
u/Xirema 18h ago
Many C-apis use void * as a type-erased pointer to user data, and you need a way to convert back and forth between your real type and the type erased pointer.
The following use of reinterpret_cast is valid, well-defined behavior in C++.
``` struct Obj { int val; std::string name; int usefulFunction(int in) { return in + val; } };
void manip_func(void * ptr, int val) { Obj& obj = reinterpret_cast<Obj>(ptr); std::println("The calculated value is {}.", obj.usefulFunction()); }
int main() { Obj obj{.val=15, .name="Sup."}; c_style_func_taking_func_pointer_and_user_pointer(&obj, &manip_func); //Will run the function in obj } ```
0
1
u/Zwischenschach25 18h ago edited 18h ago
As someone else has said, sometimes you want to deal with things as a sequence of bytes. Or just convert data from multiple sources into a common format
1
u/ekchew 16h ago
One use case I can think of is in packing/unpacking a SIMD register. SIMD implementations are language extensions that lie outside the standard, but every implementation I've ever encountered explicitly allows for getting at the scalars inside the register this way. A union can also be used to overlay different views of the memory in this context, where that would definitely be UB within the standard.
1
u/Liam_Mercier 14h ago
It's not undefined behavior if T1 is type accessible through T2 (and the object lifetime hasn't ended, and the alignment is correct, etc)
1
u/Dan13l_N 6h ago
std::memcpy really copies bytes and it makes the code a bit slower. People use C++ in environments where every microsecond is critical.
1
0
u/SoSKatan 18h ago
Well one reason that comes to mind (there are lots more) is dealing with old C style callbacks that take a type less pointer.
Sure such systems can be modernized, but you have less options if it’s library.
And sure you could just use a c style cast to interact with the c style interface, but I prefer to use reinterpret_cast to make it more clear what’s occurring. Also reinterpret_cast is searchable, which is nice for finding places that can use a clean up later on.
2
u/D3ADFAC3 18h ago
Why don’t you use static_cast for this?
2
u/SoSKatan 18h ago
Yes static_cast can be used. you can also do the same with a c style cast. But you should always use the cast that makes the intention clear.
For example, if we went by your and OP’s logic, we don’t need const_cast because static_cast can work just as well. But using const_cast should always be preferred if you are only casting the constness away.
In the example i provided, reinterpret_cast makes the intent clear and as I mentioned above, it’s more searchable. Static_cast can be used for far too many other purposes
2
u/leirus 18h ago
I disagree. Static cast is much safer and should be always preferred over more aggressive reinterpret cast
2
u/SoSKatan 18h ago
Safer? That’s an interesting word to use here.
Can you please elaborate on how exactly it’s safer to use static_cast versus reinterpret_cast in the context I provided?
In the final compiled code both do exactly the same thing, and both are type unsafe in that if you do a refactor of the type and forget to also fix the cast, it’s a bug.
Which means the only difference here is in readability and searchability of the code, and in that these example, switching one pointer to another pointer is explicitly one of the use cases for reinterpret cast.
It feels like maybe some people just don’t understand reinterpret cast and just avoid it?
That’s fine, but my response to OP’s question is valid. There are other use cases for reinterpret that isn’t covered by bit_cast.
2
u/leirus 16h ago
I can gladly elaborate.
Can you please elaborate on how exactly it’s safer to use static_cast versus reinterpret_cast in the context I provided?
In every context using static_cast is safer than reinterpret_cast becasue it does not matter what code it compiles to, but what checks compiler is doing during that compliation. For interfacing with C libraries usign static_cast is the standard way of casting from and to void*. In this particular case there are not extra checks done by static_cast, but its a good habit to use the least destructive measure that does the job. Reinterpret_cast should definitely be used sparsely.
1
u/SoSKatan 16h ago
So earlier you said static cast was not only safer than reinterpret cast but that it’s MUCH safer, but here you claim there is no difference in checks. If you were honest, you would agree your comment above is incorrect, but you failed to do that.
I take your tone to mean you don’t like nor understand what reinterpret cast is for which is fine, but then why jump in and comment about something you don’t use or understand.
Reinterpret cast was made for things like void * casting.
You said it should be used sparsely without providing a single example where you think it should be used.
I just take that to mean, you personally NEVER use it.
static cast is not better here nor is it “safer”
I want to be able to search for reinterpret cast is used for pointer type conversions, that way I can refactor the code to make it more type safe. I can’t do that with static_cast because it’s used as a kitchen sink for everything under the sun.
3
u/leirus 16h ago
Reinterpret cast was made for things like void * casting.
I strongly disagree, thats literally what static_cast was created for. Reinterpret_cast can be used for stuff like casting between pointers of different types (not void*) or casting from pointer to its intergral representation. Static_cast cant do those things.
0
u/johnnyb2001 13h ago
If you have a memory address that you want to assign to a pointer then use reinterpret cast on the memory address.
1
33
u/SoerenNissen 18h ago
It’s not -always- ub. Only in 95 percent of cases.