如何使用lambda作为std :: unique_ptr的Deleter?

时间:2018-09-05 03:08:33

标签: c++ c++11 lambda smart-pointers unique-ptr

检查以下人为程序:

#include <functional>
#include <memory>

template<typename T>
using UniPtr = std::unique_ptr<T, std::function<void(T*)>>;

int* alloc()
{
    return new int;
}

UniPtr<int> func()
{
    auto dealloc = [](int* p){delete p;};

    return UniPtr<int>{alloc(), dealloc};
}

int main()
{
    auto p = func();
    return 0;
}

std::function constructor manual开始,我认为构造std::function对象可能会引发异常,即使比率很低:

UniPtr<int> func()
{
    auto dealloc = [](int* p){delete p;};

    return UniPtr<int>{alloc(), dealloc};
}

但是如果使用函数指针而不是std::function对象:

template<typename T>
using UniPtr = std::unique_ptr<T, void(*)(T*)>;

我认为在离开func()范围之后,dealloc对象应该被释放,并且不能被引用。如果我错了,请纠正我。因此,我可以得出的唯一安全方法是定义全局dealloc函数:

void dealloc(int* p)
{
    delete p;
}

但是我不喜欢这种方法。

基于先例说明,没有100%安全的方式将lambda用作std::unique_ptr's Deleter,还是我误解了?如何将lambda用作std::unique_ptr的{​​{1}}?

3 个答案:

答案 0 :(得分:4)

  

我认为在离开func()范围之后,dealloc对象应该被释放,并且不能被引用。

您无需担心。是的,lambda对象将被销毁,但是lambda's function pointer conversion function返回的指向函数的指针始终有效,不会悬空。

  

此转换函数返回的值是指向具有C ++语言链接的函数的指针,该函数在被调用时与直接调用闭包对象的函数调用运算符具有相同的作用。

答案 1 :(得分:2)

如果您将UniPtr定义为

template<typename T>
using UniPtr = std::unique_ptr<T, void(*)(T*)>;

那么以下代码是有效的,无需担心删除程序的寿命

UniPtr<int> func()
{
    auto dealloc = [](int* p){delete p;};
    return UniPtr<int>{alloc(), dealloc};
}

引用N3337,expr.prim.lambda/6

  

没有 lambda-capture lambda-expression 的闭包类型具有公共非虚拟非显式const转换函数,该指针指向该函数具有与闭包类型的函数调用运算符相同的参数和返回类型。 此转换函数返回的值应为函数的地址,该函数在被调用时与调用闭包类型的函数调用运算符具有相同的作用。

因此,您的删除程序正在使用指向函数的指针进行初始化,即使您从func返回后,该指针仍然有效。

答案 2 :(得分:0)

为以前的答案做了一些补充...

template<typename T, typename D>
std::unique_ptr<T, D> make_unique_with_deleter(T* t, D d)
{
    // granted copy elison since C++17
    return std::unique_ptr<T, D> (t, d);
}

使用:

class A
{
};

auto up1 = make_unique_with_deleter(new A, [](A* a) {delete a; });
auto up2 = make_unique_with_deleter(std::fopen("something", "w"), std::fclose);

{
    int any_value = 0;
    // caution: only local use with lambda-capture, but possible
    auto up3 = make_unique_with_deleter(new A, [any_value](A* a) {delete a; });
}

快速解决方案。它适用于不同的情况。它以很少但不必要的开销避免了在std:function上的使用。