Question Details

No question body available.

Tags

c++ language-lawyer c++20 placement-new contiguous

Answers (2)

June 5, 2026 Score: 3 Rep: 796 Quality: Medium Completeness: 80%

Reading through the definition of the sizeof operation and also this question about memory ordering (and my memories about the memory layout in plain C), I come to the following conclusion:

There is nothing like

(...) bytes outside the object representation

Everything from the first B to the last B is part of the object and thus is captured by sizeof.
Even though I couldn't find a definition, contiguous means that the data members are all side-by-side and in the defined order.
For your first sandbox example, it would mean the following:
When you have a typed pointer to Contiguous::a (int) and increment the pointer by one (implicitly sizeof(int)), that it would point to the memory of Contiguous::b (which is also an int).

This isn't true for NotContiguous due to the different access modifiers, like it is pointed out by the linked question (which quotes the c++ standard):

Nonstatic data members of a (non-union) class declared without an intervening access-specifier are allocated so that later members have higher addresses within a class object. The order of allocation of nonstatic data members separated by an access-specifier is unspecified

The sandbox example shows that the total size is still the same (at least for the chosen compiler). I am not completely sure if the compilers are enforced to still put the two data members side-by-side or if they just do it because it makes sense.
The important part is, that the contigouty is broken because of the different access modifiers to a and b. b is allowed to be put before a and repeating the previous example could start on the second member and move the pointer beyond the range of the NotContiguous instance.

In any case, you can rely on the size returned by sizeof to reserve memory for placement-new for single elements or an array of elements.
You just many not know how the data inside of the objects is arranged.

June 5, 2026 Score: 0 Rep: 19,331 Quality: Medium Completeness: 100%

I do not see a problem with in-place construction. In the BBBxxB example, if the xx bytes are required in every object of the same type, then they are padding and counted by sizeof. If they are not required, then BBBxxB could be condensed to BBBB when required.

Do not confuse "may be non-contiguous" with "must be non-contiguous".

What sizeof yields

A second issue, is that sizeof return is unclear (for me) for non contiguous types (see https://eel.is/c++draft/expr.sizeof).

What is not clear about it?

When applied to a class, the result is the number of bytes in an object of that class including any padding required for placing objects of that type in an array.

Given a type T, place objects of that type in an array, say T arr[2]. Since an array is necessarily contiguous, the number of bytes between &arr[0] and &arr[1] is the number of bytes required for placing objects of T in an array. (Any required "bytes outside the object representation" yet inside the bounds of the object become padding.) In other words, sizeof(T) equals what you would calculate for footprint().

Since sizeof(T) is also defined to yield the number of bytes occupied by a non-potentially-overlapping object of type T, one can conclude that the number of bytes occupied by a non-potentially overlapping object of type T is the number of bytes required for placing objects of T in an array. As a corollary, any padding bytes required for placement in an array are occupied by the object.

An object that is not contiguous

Note: so far, I could not create a type that was not contiguous, in practice.

If you mean a type whose objects are never contiguous, you have a hefty task in front of you. Arrays are contiguously allocated, so being able to construct an array of a given type means that the type can have contiguous objects (the elements of the array, for example). You might get somewhere with an abstract class, but I'll leave that to someone else.

If you merely mean a type whose objects might be non-contiguous, it's not that hard. Virtual inheritance makes this rather natural.

struct Base { char z; };

struct Derived1 : virtual Base { int x; }; struct Derived2 : virtual Base { float y; };

struct Composite : Derived1, Derived2 {};

Composite object; Derived1 &sub1 = object;

In my trial run, I had output the addresses of sub1 and sub1.z and observed a difference of 28, even though sizeof(sub1) was only 16. Based on my knowledge of the addressing scheme, I conclude that sub1 occupied 16 non-contiguous bytes. The layout was probably something like the following.

x y z ------------ | Base | ------------ |----------| | Derived1 | | Derived1 |