Reputation: 132128
It appears that in C++20, we're getting some additional utility functions for smart pointers, including:
template<class T> unique_ptr<T> make_unique_for_overwrite();
template<class T> unique_ptr<T> make_unique_for_overwrite(size_t n);
and the same for std::make_shared
with std::shared_ptr
. Why aren't the existing functions:
template<class T, class... Args> unique_ptr<T> make_unique(Args&&... args); // with empty Args
template<class T> unique_ptr<T> make_unique(size_t n);
enough? Don't the existing ones use the default constructor for the object?
Note: In earlier proposals of these functions, the name was make_unique_default_init()
.
Upvotes: 32
Views: 9506
Reputation: 21513
An easy way to think of it, at least for C-devs: make_unique
use calloc()
while make_unique_for_overwrite
use malloc()
and the point of its existence is that malloc is often (always?) faster than calloc, because calloc is effectively memset(malloc(size),0,size)
Upvotes: 6
Reputation: 132128
These new functions are different:
make_XYZ
: Always initializes the pointed-to value ("explicit initialization", see § class.expl.init in the standard).make_XYZ_for_overwrite
: Performs "default initialization" of the pointed-to value (see § dcl.init, paragraph 7 in the standard); on typical machines, this means effectively no initialization for non-class, non-array types. (Yes, the term is a bit confusing; please read the paragraph at the link.)This is a feature of plain vanilla pointers which was not available with the smart pointer utility functions: With regular pointers you can just allocate without actually initializing the pointed-to value:
new int
For unique/shared pointers you could only achieve this by wrapping an existing pointer, as in:
std::unique_ptr<int[]>(new int[n])
now we have a wrapper function for that.
Note: See the relevant ISO C++ WG21 proposal as well as this SO answer
Upvotes: 30
Reputation: 303576
allocate_shared
, make_shared
, and make_unique
all initialize the underlying object by performning something equivalent to new T(args...)
. In the zero-argument case, that reduces to new T()
- which is to say, it performs value initialization. Value initialization in many cases (including scalar types like int
and char
, arrays of them, and aggregates of them) performs zero initialization - which is to say, that is actual work being done to zero out a bunch of data.
Maybe you want that and that is important to your application, maybe you don't. From P1020R1, the paper that introduced the functions originally named make_unique_default_init
, make_shared_default_init
, and allocate_shared_default_init
(these were renamed from meow_default_init
to meow_for_overwrite
during the national ballot commenting process for C++20):
It is not uncommon for arrays of built-in types such as
unsigned char
ordouble
to be immediately initialized by the user in their entirety after allocation. In these cases, the value initialization performed byallocate_shared
,make_shared
, andmake_unique
is redundant and hurts performance, and a way to choose default initialization is needed.
That is, if you were writing code like:
auto buffer = std::make_unique<char[]>(100);
read_data_into(buffer.get());
The value initialization performed by make_unique
, which would zero out those 100 bytes, is completely unnecessary since you're immediately overwriting it anyway.
The new meow_for_overwrite
functions instead perform default initialization since the memory used will be immediately overwritten anyway (hence the name) - which is to say the equivalent of doing new T
(without any parentheses or braces). Default initialization in those cases I mentioned earlier (like int
and char
, arrays of them, and aggregates of them) performs no initialization, which saves time.
For class types that have a user-provided default constructor, there is no difference between value initialization and default initialization: both would just invoke the default constructor. But for many other types, there can be a large difference.
Upvotes: 21