Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can std::byte replace std::aligned_storage?

C++17 introduced a new type, std::byte, so now we finally have a first-class citizen type to represent bytes in memory. Besides being a novelty in the standard, the C++ rules for object creation, start and end of life, aliasing etc. are fairly complicated an unintuitive most of the times, so whenever I feel std::byte is the right tool I also get nervous and reluctant to use it, for fear of unintentionally summoning the Undefined Behavior Balrogs.

One such case is a buffer to be used with placement new:

#include <memory>
#include <cstddef>
#include <type_traits>

struct X { double dummy[4]; char c; };

auto t1()
{
    // the old way

    std::aligned_storage_t<sizeof(X)> buffer;
    X* x = new (&buffer) X{};

    x->~X();
}

auto t2()
{
    // the new way?

    std::byte buffer[sizeof(X)];
    X* x = new (&buffer) X{};

    x->~X();
}

Is t2 perfectly safe and equivalent with t1?

In response to alignment issues, what about:

auto t3()
{
    alignas(X) std::byte buffer[sizeof(X)];

    X* x = new (&buffer) X{};
    x->~X();
}
like image 679
bolov Avatar asked Oct 28 '25 05:10

bolov


1 Answers

Is t2 perfectly safe and equivalent with t1?

No. In fact, both are bad.

t2 is bad for the reason NathanOliver indicates: it's underaligned. You'd need to write:

alignas(X) std::byte storage[sizeof(X)];

t1 also kind of has this problem, in that you almost certainly want to write aligned_storage_t<sizeof(X), alignof(X)> and not just aligned_storage_t<sizeof(X)>. If X were overaligned, you would lose that here. If X was just large but had no alignment requirement, you would end up with a relatively overaligned storage.

t1 is also bad for an especially peculiar reason: aligned_storage doesn't quite guarantee what you think it guarantees. In particular, it guarantees that an X can fit in aligned_storage<sizeof(X)>, but it does not guarantee that it can fit exactly. The specification is simply:

The member typedef type shall be a trivial standard-layout type suitable for use as uninitialized storage for any object whose size is at most Len and whose alignment is a divisor of Align.

That is, aligned_storage<16>::type is guaranteed to be at least 16 bytes, but a conforming implementation could easily give you 32. Or 4K. That in addition to the problem of using aligned_storage<16> by accident instead of aligned_storage_t<16>.

This is why P1413 exists as a paper: aligned_storage is kind of bad.


So the real answer is actually just to write something like libstdc++'s __aligned_membuf:

template <typename T>
struct storage_for {
    alignas(T) std::byte data[sizeof(T)];

    // some useful constructors and stuff, probably some getter
    // that gives you a T* or a T const*
};
like image 148
Barry Avatar answered Oct 29 '25 19:10

Barry



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!