The following Rust code:
use std::mem;fn main() {#[derive(Debug)]#[repr(C)]struct Test {y: f32,x: u32,}let a = Test { x: 1, y: 1.2 };let y = unsafe { mem::transmute::<Test, u64>(a) };let b = unsafe { mem::transmute::<u64, Test>(y) };println!("{y} {} {}", b.x, b.y);}
Works fine: 5361998234 1 1.2
The equivalent (in my opinion) C++ code
#include <iostream>struct Test {unsigned int x;float y;};int main() {Test a = Test{1, 1.2};long y = *reinterpret_cast<long *>(&a);Test t = *reinterpret_cast<Test *>(&y);std::cout << y << " " << t.x << " " << t.y << std::endl;}
does not: 1 1 1.4013e-45
I've tried all of the following different ways of doing the reinterpret_cast
#include <iostream>struct Test {unsigned int x;float y;};int main() {Test *a = new Test{1, 1.2};long y = *reinterpret_cast<long *>(&a);Test t = *reinterpret_cast<Test *>(&y);std::cout << y << " " << t.x << " " << t.y << std::endl;}
#include <iostream>struct Test {unsigned int x;float y;};int main() {Test *a = new Test{1, 1.2};long y = *reinterpret_cast<long *>(a);Test t = *reinterpret_cast<Test *>(&y);std::cout << y << t.x << " " << t.y << std::endl;}
None of these seem to work, generating different outputs for each.What am I doing wrong? What would be a correct equivalent of the Rust program?
Note: This is for purely recreational purposes, there is no reason for me to do this, I just want to.
Best Answer
In C++, reinterpret_cast
doesn't relax strict aliasing rules, so it cannot be used for punning arbitrary types. The only transmutation allowed universally is to inspect an object as a sequence of narrow character (char
or signed char
or unsigned char
and/or std::byte
).
You should instead use std::memcpy
as Richard Critten suggested in a comment. In most cases the compiler will generate the same efficient machine code, but also handle aliasing assumptions correctly.
I'm not sure if it this helps, but I wrote what seems to be a rough equivalent of Rust's std::mem::transmute
using a union, based on this reddit post: https://www.reddit.com/r/cpp/comments/1139u3v/equivalent_of_rusts_memforget_for_avoiding/j8qdy1a/
template <class To, class From>std::enable_if_t<sizeof(To) == sizeof(From) && alignof(To) == alignof(From), To>transmute(From from) {union Union {From from;To to;~Union() {}};Union un = {};un.from = std::move(from);return std::move(un.to);}