ΒΆtemplate <class T>
inline consteval size_t data_size_of() noexcept
template <class T>
inline consteval size_t data_size_of() noexcept
Description
Returns the data size of the type `T`. This is the number of bytes for the type excluding any tail padding, which is the number of bytes that can be memcpy'd into the type without potentially overwriting other objects. This is due to the fact that other objects can be placed inside tail padding of an object in some scenarios. To get the size of an object including tail padding, such as to know the storage taken in an array or as a stack variable, use [`size_of`]($sus::mem::size_of). Returns `size_t(-1)` for types where the tail padding can not be determined. In particular this is the case for union types unless and until compilers provide additional support to determine the maximum data size of all union members. From @ssbr: If type `T` has padding at its end, such as: ``` class T { i64 a; i32 b; }; ``` Then there are two ways for another type to place a field inside the padding adjacent to `b` and inside area allocated for [`size_of <T >()`]($sus::mem::size_of): 1. A subclass of a non-POD type can insert its fields into the padding of the base class. So a subclass of `T` may have its first field inside the padding adjacent to `b`: ``` class S : T { i32 c; }; ``` In this example, `size_of <S >() == size_of <T >()` because `c` sits inside the trailing padding of `T`. 2. A class with a `[[no_unique_address]]` field may insert other fields below it into the padding of the `[[no_unique_address]]` field. So a class that contains `T` as a field can insert another field into `T`: ``` class S { [[no_unique_address]] T t; i32 c; }; ``` In this example, `size_of <S >() == sizeof <T >()` because `c` sits inside the trailing padding of `T`. So the `data_size_of <T >() algorithm (to determine how much to memcpy safely) is something like: * A: find out how many bytes fit into the padding via inheritance (`struct S : T { bytes }` for all `bytes` until `size_of <T >() != size_of <S >()`). * B: find out how many bytes fit into the padding via no_unique_address (`struct S { [[no_unique_address]] T x; bytes }` for all `bytes` until `size_of <T >() != size_of <S >()`). ``` return size_of <T >() - max(A, B) ``` From @danakj: You might think that A == B however it is not required, as implementations may have different behaviour for inheritance vs `[[no_unique_address]]`. On MSVC 19, an empty class has size 1, but with the above formula: * A = 1, as the 1 byte gets reused by a subclass. * B = 0, as [[no_unique_address]] does not reuse the one byte of the empty class. The result is that the `data_size_of <T >()` should be 0, since the 1 byte _can_ be reused. In general, [[no_unique_address]] and [[msvc::no_unique_address]] doesn't appear do anything on MSVC 19, and subclasses also do not use padding bytes of the base class, with the exception of the base class being empty This may change in future versions of the compiler.
Declared at: sus/mem/size_of.h:125
Templates
- T