When using placement new in generic code to construct an object at a specified address, the usage pattern is a bit different from usual code. For example, consider this implementation of uninitialized_copy
: ([uninitialized.copy])
template <class It, class For>
For uninitialized_copy(It first, It last, For dest)
{
using T = typename std::iterator_traits<For>::value_type;
for (; first != last; ++first, (void)++dest)
::new (static_cast<void*>(std::addressof(*dest))) T(*first);
}
This post addresses the following points from the perspective of the standard:
why ::new
is used instead of just new
;
why an explicit cast to void*
is required.
Placement new allows you to construct an object in memory that's already allocated. You may want to do this for optimization when you need to construct multiple instances of an object, and it is faster not to re-allocate memory each time you need a new instance.
Placement new is used when you do not want operator new to allocate memory (you have pre-allocated it and you want to place the object there), but you do want the object to be constructed.
Placement new is a variation new operator in C++. Normal new operator does two things : (1) Allocates memory (2) Constructs an object in allocated memory. Placement new allows us to separate above two things. In placement new, we can pass a preallocated memory and construct an object in the passed memory.
With std::vector , a memory buffer of the appropriate size is allocated without any constructor calls. Then objects are constructed in place inside this buffer using "placement new".
(This answer uses N4659, the final C++17 draft.)
::new
is used instead of just new
::new
ensures that the operator new
is looked up in the global scope. In contrast, the plain new
first looks up in the scope of the class if T
is a class type (or array thereof), and only then falls back to the global scope. Per [expr.new]/9:
If the new-expression begins with a unary
::
operator, the allocation function's name is looked up in the global scope. Otherwise, if the allocated type is a class typeT
or array thereof, the allocation function's name is looked up in the scope ofT
. If this lookup fails to find the name, or if the allocated type is not a class type, the allocation function's name is looked up in the global scope.
For example, with
struct C {
void* operator new(std::size_t, void* ptr) noexcept
{
std::cout << "Hello placement new!\n";
return ptr;
}
};
The plain new
will cause this function to be found, thus printing unwanted message, whereas ::new
will still find the global function and work properly.
The global operator new(std::size_t, void*)
cannot be replaced because of [new.delete.placement]/1:
These functions are reserved; a C++ program may not define functions that displace the versions in the C++ standard library ([constraints]). The provisions of [basic.stc.dynamic] do not apply to these reserved placement forms of
operator new
andoperator delete
.
(See How should I write ISO C++ Standard conformant custom new and delete operators? for more about overloading operator new
.)
void*
is requiredAlthough the global operator new(std::size_t, void*)
may not be replaced, new versions of ::operator new
can be defined. For example, suppose that the following declaration is placed in the global scope:
void* operator new(std::size_t, int* ptr) noexcept
{
std::cout << "Hello placement new!\n";
return ptr;
}
Then ::new(ptr) T
will use this version instead of the global version, where ptr
is a int*
value. The pointer is explicitly cast to void*
to ensure that the void*
version of operator new
(which we intend to call) wins in overload resolution.
From comment:
But why do we want to call exactly global
new
forvoid*
if some type has special overload of new for itself? Seems like normal overloaded operator is more suitable - why it's not?
Normally, new
is used for allocation purposes. Allocation is something the user should have control over. The user can roll out more suitable versions for a normal new
.
In this case, however, we don't want to allocate anything — all we want to do is create an object! The placement new is more of a "hack" — its presence is largely due to the lack of syntax available for constructing an object at a specified address. We don't want the user to be able to customize anything. The language itself, however, doesn't care about this hack, though — we have to treat it specially. Of course, if we have something like construct_at
(which is coming in C++20), we will use it!
Also note that std::uninitialized_copy
is intended for the simplest case where you just want to copy construct a sequence of objects in raw allocated space. The standard containers allow you to customize not only how the elements are allocated, but also how they are constructed, by means of an allocator. Therefore, they do not generally use std::uninitialized_copy
for their elements — they call std::allocator_traits<Allocator>::construct
. This feature is used by std::scoped_allocator_adaptor
.
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