I read about
aligned_storage() on cppreference , but I couldn't figure it out. Please explain in a simpler way.
Let's break down the article you specified in paragraphs.
Provides the member typedef type, which is a
std::aligned_storage is a primitive type, that is, data is stored in it in a contiguous heap of bytes (like a structure in C), without C ++ problems. Thanks to this, it can be easily copied even through
… suitable for use as uninitialized storage …
Since the wrapper is a primitive type, it has no constructor or destructor. Accordingly, the constructor of the wrapped type will not be called either. As a result, we can only wrap primitive types (alas).
… for any object whose size is at most
Lenand whose alignment requirement is a divisor of
The wrapped type must 1) fit completely into the wrapper and 2) its valid alignment (multiplicity of the address in RAM) must be the divisor of
Let's take the following example. Let
Align = 16 . Its divisors are 1, 2, 4, 8 and 16. This means that we can put in this container:
int8_t(alignment – 1 byte),
int16_t(alignment – 2 bytes),
int32_t(alignment – 4 bytes),
int64_t(alignment – 8 bytes),
- or an SSE vector represented as
uint8_t(expected alignment – 16 bytes).
Why was alignment introduced? The processor exchanges with RAM not bytes, but blocks of a fixed size 2 n for some integer n . If the variable is aligned, then it can be transferred in the least possible number of hops. Unaligned data can cross the block boundary and require one more transfer.
In addition, some operations (fast aligned loading commands in SSE, for example) generally throw out a processor interrupt if there is no alignment to a certain boundary (for SSE – 16 bytes).
It's worth noting that alignment is equal to size only for primitive types. Composite types (structures and unions) have the same alignment as the largest field. This ensures that the longest field is aligned in size. Well, since the sizes of primitive types are equal to powers of two (that is, the alignment of large types is a multiple of the alignment of types of smaller ones), then all other fields will be aligned.
The default value of
Alignis the most stringent (the largest) alignment requirement for any object whose size is at most
Align can be omitted. Then the compiler will align as it would align the closest (but not larger) type. That is, as if we wrote
std::aligned_storage<sizeof(T), alignof(T)> , where
T is some built-in (albeit not existing) type with the size we need.
If the default value is not used,
Alignmust be the value of
alignof(T)for some type
T, or the behavior is undefined.
The third piece of text listed the types allowed for placement in a particular specialization of a wrapper with a specific size and alignment. The same paragraph imposes a restriction on the specific selected type :
Align must have a typical alignment for this type. That is, we cannot place
Align=6 , assuming that 6 and 3 are valid alignment values - this provokes undefined behavior (an example was already given about alignment for SSE above).
But this fragment also prohibits setting
Align , which is a multiple of the real alignment. For example, we cannot accommodate a
uint16_t aligned to 4, 8, 12, etc. bytes.
And yes, we can't align an SSE vector if we don't have a ready-made, 16-byte type. After all, then we are forced to use
char , whose native alignment is 1 byte (
char type is
char ). We need 16 – hello undefined behavior? And if we had a ready-made type, the compiler would align it by its own size (which coincides with the required alignment), and we don't really need
The behavior is undefined if
Len == 0.
The wrapper must have a nonzero length (otherwise nothing will fit in it).