c++c++17stdcode-standards

Pros and cons of make_unique vs direct constructor call in C++17


The function std::make_shared<T>() is most often preferred to std::shared_ptr<T> {new T}, because it will only make one allocation, as opposed to two allocations in the second example. There are a few other differences, as detailed in cppreference on std::make_shared.

To my knowledge, the case is different for std::make_unique<T>() vs. std::unique_ptr<T> {new T}. Before C++17, we needed the make_ functions to deduce template arguments, but since C++17, the compiler can derive them from the initialization statement.

Hence my question: what are the advantages and disadvantages of either approach of constructing a unique_ptr? Is there a specific case for which make_unique offers an advantage (other than template parameter deduction in earlier C++ editions)?


Solution

  • The advantages are:

    std::make_unique<T>(/* initialiser args */):

    1. Only need to write T once.
    2. Consistent stylistically if you are using make_shared as well.
    3. Also stylistically, you don't have to use the new keyword.

    std::unique_ptr<T>{ ::new T/* initialiser */ }:

    1. Supports any type of initialization, including aggregate initialization and designated initializers. Also default initialization without having to switch to make_unique_for_overwrite.
    2. If you initialize from a prvalue, you won't need a move. You can also omit one T with auto in C++23:
      std::unique_ptr<T>{ ::new auto(function_returning_a_T()) }.
      • If some of T's constructor args are by-value, this can also skip some moves:
        struct X { X(std::mutex); };  // std::mutex used as example for immovable type
        // auto x = std::make_unique<X>(std::mutex());  // Doesn't work
        auto x = std::unique_ptr<X>{ ::new X(std::mutex()) };  // Works