检查以下人为程序:
#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}}?
答案 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上的使用。