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.
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.
make_unique teaches users "never say new / delete and new[] / delete[] " without disclaimers. make_unique shares two advantages with make_shared (excluding the third advantage, increased efficiency).
It is recommended to use the 'make_unique/make_shared' function to create smart pointers. The analyzer recommends that you create a smart pointer by calling the 'make_unique' / 'make_shared' function rather than by calling a constructor accepting a raw pointer to the resource as a parameter.
std::make_uniqueConstructs an object of type T and wraps it in a std::unique_ptr. 1) Constructs a non-array type T . The arguments args are passed to the constructor of T . The function does not participate in the overload resolution if T is an array type.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With