Can std::byte replace std::aligned_storage?

浪尽此生 提交于 2020-08-07 20:01:33

问题


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

回答1:


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*
};



回答2:


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



来源:https://stackoverflow.com/questions/58288225/can-stdbyte-replace-stdaligned-storage

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!