20

There are some functions in the STL which start with the make_ prefix like std::make_pair, std::make_shared, std::make_unique etc. Why is it a better practice to use them instead of simply using the constructor ?

auto pair2 = std::pair< int, double >( 1, 2.0 );
auto pair3 = std::make_pair( 1, 2.0 );

std::shared_ptr< int > pointer1 = std::shared_ptr< int >( new int( 10 ) );
std::shared_ptr< int > pointer2 = std::make_shared< int >( 10 );
  • I just see that these functions make the code a little shorter, but is that all ?
  • Are there any other advantages ?
  • Are these functions safer to use ?
p.i.g.
  • 2,559
  • 1
  • 19
  • 38
  • 1
    Not too sure for the new smart pointers, but take a look at http://stackoverflow.com/questions/9270563/purpose-of-stdmake-pair for pairs. – Hatted Rooster Jul 05 '15 at 15:55
  • 3
    I believe there are distinct advantages for each, answered in separate existing questions. `make_pair` [works without the author knowing the types](http://stackoverflow.com/questions/9270563) involved. `make_shared` [provides exception safety](http://stackoverflow.com/questions/20895648). – Drew Dormann Jul 05 '15 at 15:55
  • It is better because you type less? – texasbruce Jul 05 '15 at 15:59
  • Now I'm wondering how many `make_` functions are in the STL... – nneonneo Jul 05 '15 at 20:30
  • Related to: [What happens when using make_shared](http://stackoverflow.com/q/24779929/1708801) – Shafik Yaghmour Jul 06 '15 at 12:13

3 Answers3

18

Aside from the benefit of enabling argument deduction (as already mentioned in other answers), there are also some other benefits.

std::make_pair<T1, T2> takes care to not simply return std::pair<T1, T2>. If you pass in a value using std::ref, then the returned pair won't store a std::reference_wrapper, it will store a reference.

std::make_shared can combine allocations. shared_ptr needs some place to hold things like the refcount, weak pointer list, etc. that cannot be stored in the shared_ptr directly. These can be combined with the object being created, in one slightly larger block rather than in two separate blocks.

std::make_shared and std::make_unique both make sure that no object gets left behind if exceptions are thrown. If you call a function as f(std::shared_ptr<int>(new int), std::shared_ptr<int>(new int)), then it's possible the compiler first allocates two int objects, and then constructs two shared_ptr<int> objects. If the second allocation fails, and no shared_ptr<int> object is set up yet to release the memory on destruction, then you have a memory leak. std::make_shared and std::make_unique combine the allocation of int and the construction of std::shared_ptr<int> in a function call, and the other allocation of int and the other construction of std::shared_ptr<int> in another function call. Function calls cannot overlap, so if the second allocation fails, there is already a shared pointer that will be destroyed, undoing the first allocation as well.

6

Although it may be subjective, one main touted benefit for this technique is:

write code against interfaces, not implementations

In essence, function template instantiation performs type deduction based on the arguments you pass, whereas class template instantiation does not. As a consequence, you wouldn't have to pass template arguments as you would when instantiating the class directly.

It should be noted though, that this is not about "saving a few characters", but rather about making your code more general, and avoiding being tied to a concrete type in your function call.

However, this is not always the case, as your std::make_shared example showed, there are still cases in which you have to pass the type as a template argument. But, as Herb Sutter points out, there are several other advantages when it comes to using std::make_shared:

  1. You should write for clarity and correctness first, and std::make_shared achieves both of those (subjective, but I agree)

  2. using std::make_shared is more efficient, because it allocates your object as well as the shared_ptr object in one go, allowing for lower allocation overhead and a likely better cache alignment.

nasser-sh
  • 3,343
  • 13
  • 25
1

make_unique hides from you "raw" pointer, what's usually a good thing - it's less error prone. make_shared may improve memory allocation for shared_ptr<X> instances. Normally when you use shared_ptr<X> constructor it will allocate memory twice, first for instance of X and second for it's internal data (e.g. reference counter). make_shared enables optimization - it will create single, internal structure comprising both X and reference counter, hence it will perform single memory allocation. And same as before it hides raw pointers.

Goofy
  • 4,637
  • 4
  • 34
  • 55
  • 2
    but as a drawback, if you keep a weak_ptr, all the memory from your shared_ptr will stay alive instead of only the ref_count block. (but that doesn't matter most of the time) – minirop Jul 06 '15 at 15:22