Reputation: 3884
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
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
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 typestd::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
andoperator 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
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