使用新对象重新分配“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>();
}
有没有一种方便的方法可以在不重新指定类型的情况下将 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>();
}