用于unique_ptr <t>成员的模板化通用设置器

时间:2018-09-29 13:50:01

标签: c++ c++17

我正在尝试创建一个通用的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参数不能解决问题)

4 个答案:

答案 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);
}

现在,此代码确实仅适用于原始指针(您必须moveunique_ptr,而不能转换shared_ptrunique_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);