@@ -557,6 +557,8 @@ auto Typeid() -> decltype(auto) {
557557struct {
558558 template <typename T>
559559 [[nodiscard]] auto cpp2_new (auto && ...args) const -> std::unique_ptr<T> {
560+ // Prefer { } to ( ) so that initializing a vector<int> with
561+ // (10), (10, 20), and (10, 20, 30) is consistent
560562 if constexpr (requires { T{CPP2_FORWARD (args)...}; }) {
561563 // This is because apparently make_unique can't deal with list
562564 // initialization of aggregates, even after P0960
@@ -571,7 +573,23 @@ struct {
571573[[maybe_unused]] struct {
572574 template <typename T>
573575 [[nodiscard]] auto cpp2_new (auto && ...args) const -> std::shared_ptr<T> {
576+ // Prefer { } to ( ) as noted for unique.new
577+ //
578+ // Note this does mean we don't get the make_shared optimization a lot
579+ // of the time -- we can restore that as soon as make_shared improves to
580+ // allow list initialization. But the make_shared optimization isn't a
581+ // huge deal anyway: it saves one allocation, but most of the cost of
582+ // shared_ptrs is copying them and the allocation cost saving is probably
583+ // outweighed by just a couple of shared_ptr copies; also, the make_shared
584+ // optimization has the potential downside of keeping the raw storage
585+ // alive longer when there are weak_ptrs. So, yes, we can and should
586+ // restore the make_shared optimization as soon as make_shared supports
587+ // list init, but I don't think it's all that important AFAIK
574588 if constexpr (requires { T{CPP2_FORWARD (args)...}; }) {
589+ // Why this calls 'unique.new': The workaround to use { } initialization
590+ // requires calling naked 'new' to allocate the object separately anyway,
591+ // so reuse the unique.new path that already does that (less code
592+ // duplication, plus encapsulate the naked 'new' in one place)
575593 return unique.cpp2_new <T>(CPP2_FORWARD (args)...);
576594 }
577595 else {
0 commit comments