使用新对象重新分配“unique_ptr”值的便捷类型推断方法

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

有没有一种方便的方法可以在不重新指定类型的情况下将 unique_ptr 的值重新分配给 new 拥有的对象?

例如:

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

当然 int 并不太碍眼,但是 foo::element_type 可能会很长,或者在重构后可能会发生变化。

所以,要使用类型推断,我们可以这样做:

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

...但这很可怕(foo::element_type 不起作用,因为 foo 不能用在常量表达式中)。

理想情况下,std::unique_ptr 会支持类似于 emplace 的转发方法:

foo.reassign(value);

这将释放旧值,并且,就像 std::vector::emplace 一样,就地构造新拥有的对象。

.....但据我所知,没有什么比 make_unique<decltype(foo)::element_type>.

更简洁的了

编辑: 为支持 operator= 的类型重新分配值的最简洁方法当然是使用 operator=:

*foo = value;`

...但我不想依赖 element_type 的可复制性(例如,我最初 运行 在尝试使用输入文件流时遇到这个问题)。

它不会是一个成员函数,但是一个自由函数基本上可以实现这个:

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);

(我认为这个解决方案不理想。)

使用推导目标类型的模板化转换运算符将参数(或对其的引用)存储到代理对象中。然后在推导出新对象后构造新对象。

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)...)};
}

由于您拥有独特的所有权,除非类型不可复制,否则您可以简单地做

*foo = value;
#include <memory>


// a class with a long and unweildy name
namespace mary {
  namespace poppins {
    struct supercalafragalisticexpialadocious
    {
    };
  }
}


int main()
{
  // what we don't want to have to do:

  auto a = std::make_unique<mary::poppins::supercalafragalisticexpialadocious>();

  // so alias the typename

  using atrocious = mary::poppins::supercalafragalisticexpialadocious;

  // same type with a shorter name
  a = std::make_unique<atrocious>();
}