我正在尝试实施一个std::unique_ptr
工厂,我可以这样使用:
auto fd = my_make_unique<fclose>(fopen("filename", "r"));
即,将删除函数作为模板参数传递。
我在C ++ 11中的最佳尝试是:
template<typename D, D deleter, typename P>
struct Deleter {
void operator()(P* ptr) {
deleter(ptr);
}
};
template<typename D, D deleter, typename P>
std::unique_ptr<P, Deleter<D, deleter, P>> my_make_unique(P* ptr)
{
return std::unique_ptr<P, Deleter<D, deleter, P>>(ptr);
}
在C ++ 14中它更清晰:
template<typename D, D deleter, typename P>
auto my_make_unique(P* ptr)
{
struct Deleter {
void operator()(P* ptr) {
deleter(ptr);
}
};
return std::unique_ptr<P, Deleter>(ptr);
}
但是这两种解决方案都要求我在&fclose
之前将fclose
的类型作为模板参数传递:
auto fd = my_make_unique<decltype(&fclose), fclose>(fopen("filename", "r"));
是否有可能摆脱C ++ 11中的decltype(&fclose)
模板参数?在C ++ 14中怎么样?
编辑:为什么这个问题不是RAII and smart pointers in C++的重复:引用的问题是关于C ++中的一般RAII技术,以及std::unique_ptr
可以用于此目的的答案之一。我已经熟悉RAII模式以及std::unique_ptr
如何成为一个解决方案,但是我关注如何在与C库交互时遇到的这种常见情况构建一个更易于使用的抽象的当前问题。
答案 0 :(得分:4)
实用方法:将删除器设为运行时参数。
template<typename P, typename D>
auto my_make_unique(P* ptr, D deleter)
{
return std::unique_ptr<P, D>(ptr, deleter);
}
int main()
{
auto fd = my_make_unique(fopen("filename", "r"), fclose);
}
答案 1 :(得分:4)
是否有可能摆脱C ++ 11中的
decltype(&fclose)
模板参数?在C ++ 14中怎么样?
不,直到C ++ 17才能摆脱该参数的类型。模板非类型参数需要一种类型,您无法推断 - 因为它必须是模板非类型参数。这是一个问题。
此外,您遇到的问题是未指定标准库中的函数地址。例如,标准库始终允许提供额外的重载,因此&fclose
可能无效。唯一真正可移植的方法是提供lambda或编写自己的包装函数:
auto my_fclose_lam = [](std::FILE* f) { std::fclose(f); }
void my_fclose_fun(std::FILE* f) { std::fclose(f); }
对于其中任何一个,最好使用C ++ 14,你可以引入一个宏,如:
#define DECL(v) decltype(v), v
auto fd = my_make_unique<DECL(my_fclose_lam)>(fopen("filename", "r"));
C ++ 17允许您至少通过template auto
将自定义函数提升为模板参数(虽然还不是lambda):
template <auto deleter, typename P>
auto my_make_unique(P* ptr)
{
struct Deleter {
void operator()(P* ptr) {
deleter(ptr);
}
};
return std::unique_ptr<P, Deleter>(ptr);
}
my_make_unique<my_fclose_fun>(fopen(...));
C ++ 20最终将允许你将lambda粘贴到那里:
my_make_unique<[](std::FILE* f){ std::fclose(f); }>(fopen(...));
错误的回答:
所以你能做的最好就是引入一个像:
#define DECL(v) decltype(v), v
auto fd = my_make_unique<DECL(&fclose)>(fopen("filename", "r"));
您是否认为这是一个好主意可能取决于您的同事。
在C ++ 17中,使用template auto
,您可以编写my_make_unique<fclose>
,这很棒:
template <auto deleter, typename P>
auto my_make_unique(P* ptr)
{
struct Deleter {
void operator()(P* ptr) {
deleter(ptr);
}
};
return std::unique_ptr<P, Deleter>(ptr);
}
答案 2 :(得分:2)
另一种解决方法是使用函数的完全签名:
template<typename T, int (*P)(T*)> //for `fclose`
auto my_make_unique(T*) { ... }
template<typename T, void (*P)(T*)> //for other function signatures
auto my_make_unique(T*) { ... }
//etc.
auto uniq = my_make_unique<File, fclose>(fopen("filename", "r"));
这不是通用解决方案,但在95%的情况下都可以使用。
答案 3 :(得分:0)
为std::unique_ptr
指针创建FILE*
的典型方法是:
auto fd = std::unique_ptr<FILE, decltype(fclose)>(fopen(...), fclose);
您可以将其包装在宏中:
#define my_make_unique(ptr, deleter) \
std::unique_ptr<std::remove_pointer<decltype<ptr>>::type, d>(ptr, deleter)
然后像这样使用它:
auto fd = my_make_unique(fopen(...), fclose);