c++c++14unique-ptrlanguage-designlist-initialization

Why is std::make_unique not implemented using list initialization?


https://en.cppreference.com/w/cpp/memory/unique_ptr/make_unique writes that std::make_unique can be implemented as

template<typename T, typename... Args>
std::unique_ptr<T> make_unique(Args&&... args)
{
    return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
}

This does not work for plain structs with no constructors. Those can be brace-initialized but don't have a non-default constructor. Example:

#include <memory>
struct point { int x, z; };
int main() { std::make_unique<point>(1, 2); }

Compiling this will have the compiler complain about lack of a 2-argument constructor, and rightly so.

I wonder, is there any technical reason not to define the function in terms of brace initialization instead? As in

template<typename T, typename... Args>
std::unique_ptr<T> make_unique(Args&&... args)
{
    return std::unique_ptr<T>(new T{std::forward<Args>(args)...});
}

That works well enough for the scenario above. Are there any other legitimate use cases this would break?

Seeing how the general trend appears to prefer braces for initialization, I would assume making braces in that template would be the canonical choice, but the fact that the standard doesn't do it might be an indication of me missing something.


Solution

  • Some classes have different behavior with the 2 initialization styles. e.g.

    std::vector<int> v1(1, 2); // 1 element with value 2
    std::vector<int> v2{1, 2}; // 2 elements with value 1 & 2
    

    There might not be enough reason to choose one prefer to another; I think the standard just choose one and state the decision explicitly.

    As the workaround, you might want to implement your own make_unique version. As you have showed, it's not a hard work.