Consider this code:
void f(char * ptr)
{
auto int_ptr = reinterpret_cast<int*>(ptr); // <---- line of interest
// use int_ptr ...
}
void example_1()
{
int i = 10;
f(reinterpret_cast<char*>(&i));
}
void example_2()
{
alignas(alignof(int)) char storage[sizeof(int)];
new (&storage) int;
f(storage);
}
line of interest with call from example_1:
Q1: On the callside the char pointer is aliasing our integer pointer. This is valid. But is it also valid to just cast it back to an int? We know an int is within its lifetime there, but consider the function is defined in another translation unit (with no linktime optimization enabled) and the context is not known. Then all the compiler sees is: an int pointer wants to alias a char pointer, and this is violating the strict aliasing rules. So is it allowed?
Q2: Considering its not allowed. We got std::launder in C++17. Its kind of a pointer optimization barrier mostly used to access an object which got placement new'ed into the storage of an object of other type or when const members are involved. Can we use it to give the compiler a hint and prevent undefined behavior?
line of interest with call from example_2:
Q3: Here std::launder should be required, since this is the std::launder use case described in Q2, right?
auto int_ptr = std::launder(reinterpret_cast<int*>(ptr));
But consider again f
is defined in another translation unit. How can the compiler know about our placement new, which happens on the callside? How can the compiler (only seeing function f) distinguish between example_1 and example_2? Or is all above just hypotetical, since the strict aliasing rule would just ruling out everything (remember char* to int* not allowed) and the compiler can do what he wants?
Follow up question:
Q4: If all code above is wrong due to aliasing rules, consider changing the function f
to take a void pointer:
void f(void* ptr)
{
auto int_ptr = reinterpret_cast<int*>(ptr);
// use int_ptr ...
}
Then we have no aliasing problem, but still there is the std::launder
case for example_2
. Do we have change the callside and rewrite our example_2
function to:
void example_2()
{
alignas(alignof(int)) char storage[sizeof(int)];
new (&storage) int;
f(std::launder(storage));
}
or is std::launder
in function f
sufficient?