antred
antred

Reputation: 3884

Placement new and alignment in C++

Consider the following code snippet constructing an instance of a POD (plain old data) struct in-place:

#include <new>
#include <cassert>
#include <cstddef>

struct Test
{
    int a;
    char b;
    double c;
};

int main()
{
    const std::size_t minimumNumberOfBytes = sizeof( Test ) * 4;

    // Get a block of memory that can accommodate a Test instance and then some!
    void* const ptrToMemBlock = new char[ minimumNumberOfBytes ];
    assert( ptrToMemBlock );

    // Construct a Test instance in-place.
    const Test* const testInstance( ::new ( ptrToMemBlock ) Test() );

    // Is this assumption guaranteed to be true?
    assert( testInstance == ptrToMemBlock );
}

Is the assumption represented by the final assert() guaranteed to always be correct? Or is it conceivable that the compiler might decide to construct the Test instance, say a few bytes after the start of the memory block I specified in the placement-new call?

Note that I'm asking specifically about POD types here. I know that things can get iffy if multiple inheritance and stuff like that gets involved.

Upvotes: 14

Views: 3829

Answers (3)

Mike Seymour
Mike Seymour

Reputation: 254681

Yes, the assertion will hold. Any new expression creating a single object must request exactly sizeof(Test) bytes of storage from the allocation function; and so it must place the object at the start of that storage in order to have enough room.

Note: This is based on the specification of a new-expression in C++11. It looks like C++14 will change the wording, so the answer may be different in the future.

Upvotes: 6

Deduplicator
Deduplicator

Reputation: 45684

Yes, the the last assert is guaranteed to hold, because this form of placement-new must always return the passed pointer, not using any space for itself:

5.3.4 New [expr.new]

8 A new-expression may obtain storage for the object by calling an allocation function (3.7.4.1). [...]
10 An implementation is allowed to omit a call to a replaceable global allocation function (18.6.1.1, 18.6.1.2). When it does so, the storage is instead provided by the implementation or provided by extending the allocation of another new-expression. [...]
11 When a new-expression calls an allocation function and that allocation has not been extended, the newexpression passes the amount of space requested to the allocation function as the first argument of type std::size_t. That argument shall be no less than the size of the object being created; it may be greater than the size of the object being created only if the object is an array.
[...]

Your new-expression calls the global placement-new allocation-function.
That is a non-replacable function, thus the allocation cannot be extended or omitted.
Also, you are not allocating an array but a single object, thus no padding of the request may occur at all.

18.6.1.3 Placement forms [new.delete.placement]

1 These functions are reserved, a C++ program may not define functions that displace the versions in the Standard C++ library (17.6.4). The provisions of (3.7.4) do not apply to these reserved placement forms of operator new and operator delete.

void* operator new(std::size_t size, void* ptr) noexcept;

2 Returns: ptr.
3 Remarks: Intentionally performs no other action.

And this guarantees that the allocation-function returns the passed pointer unchanged.

Upvotes: 7

Freddie Chopin
Freddie Chopin

Reputation: 8860

This assertion will always hold, because new is required to return blocks of memory with MAXIMUM possible alignment. BTW - your first assert() is worthless, as normal new does not return nullptr - it throws or aborts, only "nothrow new" can return nullptr.

Upvotes: 12

Related Questions