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