emplace-like function for std::unique_ptr

578 views
Skip to first unread message

Victor Dyachenko

unread,
Nov 16, 2016, 5:58:54 AM11/16/16
to ISO C++ Standard - Future Proposals
Assume we have a variable like this:

std::unique_ptr<some_long_and_ugly_name> p;

If it is a local variable then it can be constructed as

auto p = std::make_unique<some_long_and_ugly_name>(...);

without duplication of template argument (some_long_and_ugly_name).

But if it is member variable, we can't avoid duplication:

class C
{
    std
::unique_ptr<some_long_and_ugly_name> p;
    C
()
   
{
        p
= std::make_unique<some_long_and_ugly_name>(...);
   
}
};

If std::optional is used instead of std::unique_ptr then duplication can be avoided using emplace() for object construction:

class C
{
    std
::optional<some_long_and_ugly_name> p;
    C
()
   
{
        p
.emplace(...); // <-- no template argument duplication here
   
}
};

It would be nice to similar function for std::unique_ptr. The name "emplace" is not appropriate here, ptr itself doesn't contain constructed object. May be "create" or "new_"? And this function can provide strong exception guarantee, so effect of its usage won't differ from make_unique case.

class C
{
    std
::unique_ptr<some_long_and_ugly_name> p;
    C
()
   
{
        p
.new_(...);
   
}
};

Any thoughts/comments/suggestions?

Adam Foxman

unread,
Nov 16, 2016, 2:24:19 PM11/16/16
to std-pr...@isocpp.org

Are the intended semantics similar to reset(), where an existing object is replaced?

--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposal...@isocpp.org.
To post to this group, send email to std-pr...@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/437f3bb8-6c04-4107-943c-32908b54d82e%40isocpp.org.

joseph....@gmail.com

unread,
Nov 16, 2016, 6:55:17 PM11/16/16
to ISO C++ Standard - Future Proposals

How would such a function know how to create a new object for a unique_ptr with a custom deleter?

Nicol Bolas

unread,
Nov 16, 2016, 11:15:30 PM11/16/16
to ISO C++ Standard - Future Proposals, joseph....@gmail.com

The same way `unique_ptr::reset` does. In that it does not; it just uses the existing deleter object. Indeed, `unique_ptr` has no methods to change the deleter object after assigning to it.

Victor Dyachenko

unread,
Nov 17, 2016, 1:38:48 AM11/17/16
to ISO C++ Standard - Future Proposals
On Wednesday, November 16, 2016 at 10:24:19 PM UTC+3, Adam Foxman wrote:

Are the intended semantics similar to reset(), where an existing object is replaced? 

 
Intendent semantics is similar to std::optional::emplace(), so yes, existing object is replaced.

But in this case there is no need to destroy the old object before creation, so function can additionally provide strong exception safety guarantee.

Victor Dyachenko

unread,
Nov 17, 2016, 1:51:46 AM11/17/16
to ISO C++ Standard - Future Proposals, joseph....@gmail.com
On Thursday, November 17, 2016 at 2:55:17 AM UTC+3, joseph....@gmail.com wrote:

How would such a function know how to create a new object for a unique_ptr with a custom deleter?

Yes... currently no other member functions create guarded object... make_unique() is not member and handles special case with default_deleter.

May be it can be non-member fuction like this:

template<class T, class... Args>
void reallocate_unique(std::unique_ptr<T> &p, Args &&... args)
{
    p
= std::make_unique<T>(std::forward<Args>(args)...);
}

Tony V E

unread,
Nov 17, 2016, 1:54:16 AM11/17/16
to ISO C++ Standard - Future Proposals, joseph....@gmail.com
Would reset_new be a reasonable name?


Sent from my BlackBerry portable Babbage Device
From: Victor Dyachenko
Sent: Thursday, November 17, 2016 1:51 AM
To: ISO C++ Standard - Future Proposals
Subject: [std-proposals] Re: emplace-like function for std::unique_ptr

--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposal...@isocpp.org.
To post to this group, send email to std-pr...@isocpp.org.

Victor Dyachenko

unread,
Nov 17, 2016, 2:11:41 AM11/17/16
to ISO C++ Standard - Future Proposals
May be I'm initially wrong and "upgrading" unique_ptr is a bad idea?

My initial task is "late construction" of some members. std::optional is a good solution in most cases, but when member is big and/or seldom used, heap allocation is a better option. I use unique_ptr in such cases but construction suffers from verbosity and, more important, violates DRY principle.

joseph....@gmail.com

unread,
Nov 17, 2016, 2:27:06 AM11/17/16
to ISO C++ Standard - Future Proposals, joseph....@gmail.com

But unique_ptr::reset doesn't create anything; it just takes ownership of an existing object. The proposed function necessarily creates an object using operator new which is likely to be incorrect for a unique_ptr with a custom deleter.

joseph....@gmail.com

unread,
Nov 17, 2016, 3:21:02 AM11/17/16
to ISO C++ Standard - Future Proposals
On Thursday, 17 November 2016 15:11:41 UTC+8, Victor Dyachenko wrote:
May be I'm initially wrong and "upgrading" unique_ptr is a bad idea?

My initial task is "late construction" of some members. std::optional is a good solution in most cases, but when member is big and/or seldom used, heap allocation is a better option. I use unique_ptr in such cases but construction suffers from verbosity and, more important, violates DRY principle.

Here's a stupidly over-engineered solution that removes the need to specify the type:

#include <memory>
#include <tuple>
#include <type_traits>
#include <utility>

template <typename... Ts>
class make_unique_auto_proxy {
public:
   
template<typename U>
   
operator std::unique_ptr<U>() {
       
return make<U>(std::make_index_sequence<sizeof...(Ts)>());
   
}

private:
    std
::tuple<Ts...> ts;

     
template <typename... Us>
    make_unique_auto_proxy
(Us&&... us) : ts(std::forward<Ts>(us)...) {
   
}

     
template <typename U, size_t... Is>
    std
::unique_ptr<U> make(std::index_sequence<Is...>) {
       
return std::make_unique<U>(std::move(std::get<Is>(ts))...);
   
}
 
     
template <typename... Us>
   
friend make_unique_auto_proxy<Us...> make_unique_auto(Us&&...);
};

template<typename... Ts>
make_unique_auto_proxy
<Ts...> make_unique_auto(Ts&&... ts) {
   
return make_unique_auto_proxy<std::decay_t<Ts>...>(std::forward<Ts>(ts)...);
}

int main() {
   
auto u = std::make_unique<int>();
      u
= make_unique_auto(42);
}

joseph....@gmail.com

unread,
Nov 17, 2016, 3:29:42 AM11/17/16
to ISO C++ Standard - Future Proposals, joseph....@gmail.com

Stupid indentation. Here. I also changed the name to be a bit less verbose:

#include <memory>
#include <tuple>
#include <type_traits>
#include <utility>

template <typename... Ts>
class auto_unique_proxy {

public:
   
template<typename U>
   
operator std::unique_ptr<U>() {
       
return make<U>(std::make_index_sequence<sizeof...(Ts)>());
   
}

private:
    std
::tuple<Ts...> ts;

   
template <typename... Us>

    auto_unique_proxy
(Us&&... us) : ts(std::forward<Ts>(us)...) {

   
}

   
template <typename U, size_t... Is>
    std
::unique_ptr<U> make(std::index_sequence<Is...>) {
       
return std::make_unique<U>(std::move(std::get<Is>(ts))...);
   
}

   
template <typename... Us>

   
friend auto_unique_proxy<Us...> auto_unique(Us&&...);
};

template<typename... Ts>
auto_unique_proxy
<Ts...> auto_unique(Ts&&... ts) {
   
return auto_unique_proxy<std::decay_t<Ts>...>(std::forward<Ts>(ts)...);
}

int main() {
    using std::make_unique;

   
auto u = make_unique<int>();
    u
= auto_unique(42);
}


 

Arthur O'Dwyer

unread,
Nov 18, 2016, 4:41:40 PM11/18/16
to ISO C++ Standard - Future Proposals
If that's the semantics you want, then yes, it should be a non-member function, and you should just write it yourself. I don't think the standard library needs to get involved here.
As a bonus, if you control the implementation of this function, you can later decide that the semantics you *really* want are

template<class T, class... Args>
void reallocate_unique(std::unique_ptr<T>& p, Args&&... args)
{

   
if (p) {
       
*p = T(std::forward<Args>(args)...);
   
} else {

        p
= std::make_unique<T>(std::forward<Args>(args)...);
   
}
}

since it's a little silly to constantly be churning memory when you might have a perfectly good memory block already owned by p.
...And then you might change it back, because you suddenly have to deal with Ts that aren't move-assignable.
...And then you might upgrade to C++17 and rewrite the whole thing using if-constexpr so that it handles both kinds of Ts.

Anyway, you can do all this in your own codebase even if you're stuck with C++11. I don't think there's any benefit to be had by trying to get this function into the C++2w standard library.

–Arthur
Reply all
Reply to author
Forward
0 new messages