Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Convenient type-inferring way to reassign `unique_ptr` value with new object

Is there a convenient way to re-assign the value of a unique_ptr with a new owned object, without re-specifying the type?

For instance:

std::unique_ptr<int> foo;
// .... Later, once we actually have a value to store...
foo = std::make_unique<int>(my_cool_value);

Of course int is not too much of an eyesore, but foo::element_type could be long or subject to change after a refactoring.

So, to use type inference, we could do:

foo = std::make_unique<decltype(foo)::element_type>(value);

...But that's pretty hideous (foo::element_type doesn't work because foo can't be used in a constant expression).

Ideally, std::unique_ptr would support a forwarding emplace-like method:

foo.reassign(value);

This would release the old value and, just like std::vector::emplace, construct the new owned object in-place.

....But as far as I can tell, there's nothing more concise than make_unique<decltype(foo)::element_type>.

EDIT: The most concise way to reassign the value for a type that supports operator= is, of course, to use operator=:

*foo = value;`

...But I do not want to rely on the copyability of element_type (for instance, I initially ran into this issue when trying to work with input-file streams).

like image 558
Kyle Strand Avatar asked Sep 11 '25 06:09

Kyle Strand


2 Answers

Stash the arguments (or references thereto) into a proxy object with a templated conversion operator that deduces the target type. Then construct the new object once you have that deduced.

template<class... Args>
struct maker {
    template<class T>
    operator std::unique_ptr<T>() && {
        return make<T>(std::index_sequence_for<Args...>());
    }
    std::tuple<Args...> args;
private:  
    template<class T, size_t ... Is>
    std::unique_ptr<T> make(std::index_sequence<Is...>) {
        return std::make_unique<T>(std::get<Is>(std::move(args))...);
    }

};

template<class... Args>
auto maybe_make_unique_eventually(Args&&... args){
    return maker<Args&&...>{std::forward_as_tuple(std::forward<Args>(args)...)};
}
like image 177
T.C. Avatar answered Sep 12 '25 20:09

T.C.


It won't be a member function, but a free function could essentially achieve this:

template<typename T, typename D, typename...Args>
void TakeNew(std::unique_ptr<T,D>& up, Args&&... args)
{
  up.reset(new T{std::forward<Args>(args)...});
  // or use parentheses for consistency with `make_unique`; see comments
}

// usage...
auto foo = std::make_unique<int>(3);
// .... Later...
TakeNew(foo, 5);

(I do not consider this solution ideal.)

like image 30
Kyle Strand Avatar answered Sep 12 '25 22:09

Kyle Strand