make_unique with brace initialization

In C++20, this will compile:

std::make_unique<point>(1, 2);

due to the new rule allowing initializing aggregates from a parenthesized list of values.


In C++17, you can just do:

std::unique_ptr<point>(new point{1, 2});

That won't work with make_shared though. So you can also just create a factory (forwarding left as an exercise):

template <typename... Args>
struct braced_init {
    braced_init(Args... args) : args(args...) { }
    std::tuple<Args...> args;

    template <typename T>
    operator T() const {
        return std::apply([](Args... args){
            return T{args...};
        }, args);
    }
};

std::make_unique<point>(braced_init(1, 2));

In C++14, you'll have to implement apply and write a factory function for braced_init because there's no CTAD yet - but these are doable.


Seeing how the general trend appears to prefer braces for initialization

Citation needed. It's a charged topic - but I definitely disagree with the claim.


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.


In addition to other answers, in his presentation on C++17, Alisdair Meredith gives the following implementation of make_unique:

template<typename T, typename... Args>
auto make_unique(Args&&... args) -> std::unique_ptr<T> {
    if constexpr (std::is_constructible<T, Args...>::value)
        return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
    else
        return std::unique_ptr<T>(new T{std::forward<Args>(args)...});
}

It uses C+17 if constexpr, but can easily be rewritten without it.

With this version you can do both

auto v = make_unique<std::vector<int>>(10, 20); // *v is a vector of 10 elements

and

auto p = make_unique<point>(10, 20); // *p is a point (10, 20)