Zaaier
Zaaier

Reputation: 736

Does separating allocation from initialization in this way cause undefined behavior?

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

Answers (1)

Ted Lyngmo
Ted Lyngmo

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

Related Questions