I read about aligned_storage() on cppreference , but I could not understand it. Explain, please, easier.

1 answer 1

Let's look at the paragraph you specified in paragraphs.

Provides the member typedef type, which is a PODType ...

The wrapper std::aligned_storage is a primitive type, that is, the data is stored in it in a continuous bunch of bytes (as a structure in C), without C ++ - problems. Because of this, you can safely copy it through memcpy .

... suitable for use as uninitialized storage ...

Since the wrapper is a primitive type, it has neither a constructor nor a destructor. Accordingly, the constructor of the type being wrapped will not be called either. As a result, we can only wrap primitive types (alas).

It is a divisor of Align .

The type to be wrapped must 1) fit completely into the wrapper and 2) its allowable alignment (the multiplicity of the address in the RAM) must be an Align divisor.

Take the following example. Let Align = 16 . Its dividers are 1, 2, 4, 8 and 16. This means that we can put in this container:

  • char / uint8_t / int8_t (alignment - 1 byte),
  • uint16_t / int16_t (alignment - 2 bytes),
  • uint32_t / int32_t (alignment - 4 bytes),
  • uint64_t / int64_t (alignment - 8 bytes),
  • or SSE vector represented as uint8_t[16] (expected alignment is 16 bytes).

Why was the alignment introduced? The processor exchanges with RAM not bytes, but blocks of fixed size 2 n with some whole n . If a variable is aligned, then it can be transmitted in the least possible number of shipments. Unaligned data, however, may cross the block boundary and require one more transfer.

In addition, some operations (commands for fast aligned load in SSE, for example) generally throw out a processor interrupt when there is no alignment to a certain boundary (for SSE, 16 bytes).

It should be noted that the alignment is equal to the size only for primitive types. For compound types (structures and unions), the alignment is equal to that of the largest field. This ensures that the longest field will be aligned in size. Well, since the sizes of primitive types are equal to a power of two (that is, the alignment of large types is a multiple of the alignment of types of smaller ones), all the other fields will be aligned.

The size of the line is the most stringent.

Align can be omitted. Then the compiler will align as it would level the nearest (but not exceeding) size type. That is, if we wrote std::aligned_storage<sizeof(T), alignof(T)> , where T is a kind of built-in (albeit not existing) type with the size we need.

If the default value is not used, it must be alignof(T) .

The third piece of text listed the types that are valid for placement in a particular specialization wrapper with a specific size and alignment. The same paragraph imposes a restriction on the specific type chosen : Align must have an alignment typical of this type. That is, we cannot place uint16_t with Align=6 , assuming that 6 and 3 are valid alignment values ​​- this provokes undefined behavior (an example about alignment for SSE was already given above).

But this fragment also prohibits setting the Align multiple of this alignment. For example, we cannot place uint16_t with alignment at 4, 8, 12, etc. bytes.

And yes, we cannot align the SSE vector if we do not have a ready, 16-byte type. After all, then we are forced to use char[16] , whose native alignment is 1 byte ( char type is char ). We also need 16 - hello, indefinite behavior? But if we have a ready type, the compiler itself aligns it to its own size (which coincides with the required alignment), and we don’t need std::aligned_storage .

The behavior is undefined if Len == 0 .

The wrapper must have a non-zero length (otherwise nothing will fit in it).

  • one
    Wait, it seems, on the contrary? We have the alignment requirement = 16, and it is not divisor 2. On the contrary, 2 is divisor 16. - VladD
  • @VladD, reread - it came. This is not an alignment calculation rule (as I thought at the beginning), it is a restriction on the valid wrapped types. We can wrap any type whose alignment is a multiple of the divider Len . - ߊߚߤߘ
  • @VladD, although no, not “multiple”, but “strictly equal”. I would like to know the details regarding this moment. Where does this restriction on multiplicity come from? After all, even the alignment of the 4-byte uint32_t to the 8-byte boundary should be valid. - ߊߚߤߘ