Reputation: 736
Does the following separation of declaration/allocation from initialization result in undefined behavior?
#include <new>
#include <utility>
template<typename T>
T Uninitialized()
{
alignas(T) char bytes[sizeof(T)];
return std::forward<T>(*(T*)(void*)(&bytes));
}
template<typename T>
T ZeroInitialized()
{
alignas(T) char bytes[sizeof(T)] = {};
return std::forward<T>(*(T*)(void*)(&bytes));
}
struct S
{
char c = 1;
};
int main()
{
// This arrangement lets us defer (or skip!) construction of
// these stack-allocated variables.
S sU = Uninitialized<S>();
S sZ = ZeroInitialized<S>();
{
// Note that without using placement new, the destructors of
// sU and sZ would be invoked at the end of this block scope
// AND at the end of `main`.
new (&sU) S();
new (&sZ) S();
}
return 0;
}
UBSan doesn't object, but that isn't a guarantee of correctness.
(edit) related discussions:
Copying structs with uninitialized members
Is circumventing a class' constructor legal or does it result in undefined behaviour?
Upvotes: 1
Views: 78
Reputation: 117298
This arrangement lets us defer (or skip!) construction of these stack-allocated variables.
No, it does not. Both allocation functions move constructs T
:
return std::forward<T>(*(T*)(void*)(&bytes));
// ^
Given that it does so from a plain buffer of bytes, constructing a non-POD T
will make it have undefined behavior. In the Uninitialized
version it will not matter if it's a POD since it'll read from uninitialized memory and therefore have undefined behavior.
Upvotes: 3