Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What does std::make_unique_for_overwrite() do vis-a-vis std::make_unique()?

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().

like image 430
einpoklum Avatar asked Sep 22 '19 15:09

einpoklum


People also ask

What is the use of std :: Make_unique?

make_unique prevents the unspecified-evaluation-order leak triggered by expressions like foo(unique_ptr<X>(new X), unique_ptr<Y>(new Y)) . (Following the advice "never say new " is simpler than "never say new , unless you immediately give it to a named unique_ptr ".)

What does Make_unique return C++?

class A {} std::unique_ptr<A> f() { return std::make_unique<A>(); } std::unique_ptr<A> a = f(); Hmm, if I remember correctly, a function in C++ returns a copy of the returned object. A function f returns a copy of std::unique_ptr constructed by std::make_unique . How is it possible?

Does Make_unique initialize?

Yes, all the elements will be value initialized by std::make_unique. This is the initialization performed when a variable is constructed with an empty initializer.

When was Make_unique introduced?

Introduction. The std::make_unique function was introduced in the C++14 standard.


Video Answer


2 Answers

These new functions are different:

  • Original make_XYZ: Always initializes the pointed-to value ("explicit initialization", see § class.expl.init in the standard).
  • New 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

like image 139
einpoklum Avatar answered Oct 20 '22 00:10

einpoklum


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 or double to be immediately initialized by the user in their entirety after allocation. In these cases, the value initialization performed by allocate_shared, make_shared, and make_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.

like image 23
Barry Avatar answered Oct 20 '22 02:10

Barry