During a dive into dynamic memory, it occurred to me it appears contradictory how trivial types begin their lifetime. Consider the snippet
void* p = ::operator new(sizeof(int)); // 1
// 2
new (p) int; // 3
When does the int
start its lifetime?
Only acquires storage, ::operator new
is specified to have the effect (from [new.delete.single])
The allocation functions called by a new-expression to allocate size bytes of storage. [...] allocates storage suitably aligned to represent any object of that size provided the object's type does not have new-extended alignment.
Given that acquiring storage is insufficient in creating an object, the int
cannot have begin its lifetime here.
At this point, suitbale storage for the int
has been acquired.
The int
is created by placement new. But somehow its lifetime didn't begin here, since from [basic.life]
[...] An object is said to have non-vacuous initialization if it is of a class or aggregate type and it or one of its subobjects is initialized by a constructor other than a trivial default constructor. The lifetime of an object of type
T
begins when:
storage with the proper alignment and size for type
T
is obtained, andif the object has non-vacuous initialization, its initialization is complete [...]
int
is neither a class nor aggregate type, hence it has vacuous initialization. Therefore only the first bullet applies. However, this is clearly not when storage is obtained and therefore cannot be when its lifetime starts.
Allocators are required to return memory without constructing its elements. Yet this doesn't make sense with trivial types. The effects of a.allocate(n)
with a
an allocator object for type T
is
Memory is allocated for
n
objects of typeT
but objects are not constructed.
The lifetime of a temporary object may be extended by binding to a const lvalue reference or to an rvalue reference (since C++11), see reference initialization for details.
C/C++ use lexical scoping. The lifetime of a variable or object is the time period in which the variable/object has valid memory. Lifetime is also called "allocation method" or "storage duration."
Technically, the new-expression always obtains storage. The code new(p) int
both obtains storage and creates an object, and according to [basic.life]/1, the object's lifetime began when new(p) int
obtained the storage.
According to N4659 [expr.new], the code new(p) int
generates a call to an allocation function ::operator new(sizeof(int), p)
. And under [new.delete.placement], the standard library defines such a function:
void* operator new(std::size_t size, void* ptr) noexcept;
Returns: ptr.
Remarks: Intentionally performs no other action.
Although "no other action" is performed, and probably the implementation will optimize out any actual function call, this call to an allocation function still counts as obtaining storage for the object being created by the new-expression.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With