我正在尝试创建一个通用的setter,支持值和指针。 我在使用指针时遇到麻烦。最少的代码阐明了主要思想:
#include <memory>
#include <variant>
class A{
int s;
};
template <typename uniq, typename ptr>
void set(uniq &u, const ptr &p){
u = std::unique_ptr<decltype(*std::declval<ptr>)>(p);
}
int main(){
std::variant<std::unique_ptr<A>> dd;
set(dd, new A);
}
编译(gcc-7.3.0)失败
no matching function for call to ‘std::unique_ptr<A*&& (&)() noexcept, std::default_delete<A*&& (&)() noexcept> >::unique_ptr(A* const&)’
我对如何获取指针类型一无所知... 另外,引用来自何处? (更改ptr参数不能解决问题)
答案 0 :(得分:6)
您在这里有一个错字:
template <typename uniq, typename ptr>
void set(uniq &u, const ptr &p){
u = std::unique_ptr<decltype(*std::declval<ptr>)>(p);
// ^^^
}
std::declval
是一个函数,因此您正在编写的代码正试图取消引用该函数类型。这是一件有效的事情!这就是您获得A*&& (&)() noexcept
您想要的是:
template <typename uniq, typename ptr>
void set(uniq &u, const ptr &p){
u = std::unique_ptr<decltype(*std::declval<ptr>())>(p);
}
您可以直接使用名称p
来简化很多工作:
template <typename uniq, typename ptr>
void set(uniq &u, const ptr &p){
u = std::unique_ptr<decltype(*p)>(p);
}
但这实际上可能不正确,因为decltype(*p)
通常具有引用类型,因此您必须将其删除:
template <typename uniq, typename ptr>
void set(uniq &u, const ptr &p){
u = std::unique_ptr<std::remove_reference_t<decltype(*p)>>(p);
}
现在,此代码确实仅适用于原始指针(您必须move
和unique_ptr
,而不能转换shared_ptr
到unique_ptr
),因此您可以在函数签名中更清楚地表达这一点,这也使主体易于编写:
template <typename uniq, typename T>
void set(uniq &u, T* p){
u = std::unique_ptr<T>(p);
}
最终会问出您为什么要写的问题:
set(dd, new A);
代替:
dd = std::make_unique<A>();
反正?第二名要好得多。
答案 1 :(得分:2)
要显式构造std::unique_ptr
:
template <typename uniq, typename ptr>
void set(uniq &u, const ptr &p)
{
using T = std::remove_pointer_t<ptr>;
u = std::unique_ptr<T>(p);
}
答案 2 :(得分:2)
实现有点奇怪。如果您知道要在unique_ptr
上设置u
,那么为什么模板化为uniq
而不是std::unique_ptr<type>
?
考虑到我上面写的内容,我将其实现为
template <class type>
void set(std::unique_ptr<type>& unique_pointer, type* pointer) {
unique_pointer.reset(pointer);
}
template <class type, class ... argument_types>
void set(std::unique_ptr<type>& unique_pointer, argument_types&& ... args) {
unique_pointer = std::make_unique<type>(std::forward<argument_types>(args)...);
}
int main() {
std::unique_ptr<std::string> up0, up1, up2, up3;
set(up0);
set(up1, "hello world");
set(up2, " hello world ", 3, 11);
set(up3, new std::string("hello world"));
return 0;
}
答案 3 :(得分:0)
您可以使用std::unique_ptr<>::reset
:
std::variant<std::unique_ptr<A>> u;
A* p = new A;
std::get<0>(u).reset(p);
或者:
using unique_ptr_type = typename std::decay<decltype(std::get<0>(u))>::type;
u = unique_ptr_type(p);