问题
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 witht1
?
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 mostLen
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 witht1
?
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 see Barry's answerstd::aligned_storage
来源:https://stackoverflow.com/questions/58288225/can-stdbyte-replace-stdaligned-storage