4

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();
}

2 Answers 2

9

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*
};
Sign up to request clarification or add additional context in comments.

Comments

5

Is t2 perfectly safe and equivalent with t1?

No. std::aligned_storage creates storage that is suitably aligned for the object being placed in it. std::byte buffer[sizeof(X)] while being the correct size, has the alignment of std::byte. This normally will not have same alignment of the type you are placing in it since it has an alignment of 1.

This is not an issue as far as the C++ virtual machine is concerned, but in the real world this can cause sever performance penalties or even cause the program to crash.

If you want suitably aligned storage, use std::aligned_storage see Barry's answer

9 Comments

spitballing here: what about alignas(alignof(X)) std::byte buffer[sizeof(X)];?
@bolov That would be fine then. That's a lot more typing than std::aligned_storage_t<sizeof(X)> buffer though.
@NathanOliver: But it would be potentially better, since alignof(X) may be far less than the default alignment that aligned_storage gives you without specifying an alignment.
@NicolBolas For that you can use std::aligned_storage_t<sizeof(X), alignof(X)> buffer
@NathanOliver: Also, aligned_storage/union are being deprecated in C++20 or 23.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.