我尝试将std::unique_ptr
与自定义删除工具一起使用,以简化管理从各种C API返回给我的句柄的生命周期。理论上这很好,但是我很难找到一种在运行时都是最优的方法,并且没有为每种包装类型提供一堆样板。
例如,考虑一些不透明的类型foo
,必须通过将其指针传递给destroy_foo
来释放它:
// approach 1: pass destroy_foo at runtime (bad for performance)
using foo_ptr = std::unique_ptr<foo, decltype(&destroy_foo)>;
foo_ptr bar{create_foo(...), destroy_foo};
// approach 2: make a deleter type (verbose - for many types)
struct foo_deleter
{
void operator()(foo* p)
{
destroy_foo(p);
}
};
using foo_ptr = std::unique_ptr<foo, foo_deleter>;
foo_ptr bar{create_foo(...)};
第一种方法很难让编译器进行优化,因为我传递了一个函数指针,所以它就出来了。第二种方法似乎不必要地冗长。我有很多这样的类型我想管理,并为每一个手动创建一个类是痛苦的。
如何定义一个带destroy_foo
的类模板,并为我提供一个等同于foo_deleter
的类型?或者是否有标准的库模板来执行此操作?
// best of both worlds - to_obj<Func> makes foo_deleter from destroy_foo...
using foo_ptr = std::unique_ptr<foo, to_obj<destroy_foo>>;
foo_ptr bar{create_foo(..)};
因此,给定任何函数,模板将定义一个带有operator()
的类,它只是将所有参数转发给函数,并返回结果。
答案 0 :(得分:4)
像
这样的东西template<typename T, void (*func)(T*)>
struct Deleter{
void operator()(T* t) { func(t); }
};
...
或者如果你想要更强大的东西
template <typename t>
struct function_traits;
template <typename R, typename A>
struct function_traits<R (*)(A)>
{
using t_ret = R;
using t_arg = A;
};
template <typename F, F* f>
struct Functor
{
using FT = function_traits<F*>;
typename FT::t_ret operator()(typename FT::t_arg a) {
return f(a);
}
};
void mydeleter(int*);
#define FUNCTOR(F) Functor<decltype(F),&F>
或者使用C ++ 11的全部功能
template <typename F, F* f>
struct Functor
{
template<typename... A>
auto operator()(A&&... a) -> decltype(f(std::forward<A>(a)...)) {
return f(std::forward<A>(a)...);
}
};
#define FUNCTOR(F) Functor<decltype(F),&F>
答案 1 :(得分:1)
如果我理解你的问题,你是否正在寻找类似的东西?
#include <iostream>
#include <memory>
template <typename TYPE>
void custom_deletor(TYPE * t)
{
delete t;
}
template <typename TYPE>
struct delete_functor
{
void operator () (TYPE * o)
{
custom_deletor(o);
}
};
template <typename TYPE>
std::unique_ptr<TYPE, delete_functor<TYPE>> make_unique(TYPE * data)
{
return std::unique_ptr<TYPE, delete_functor<TYPE>>(data, delete_functor<TYPE>());
}
struct foo
{
static void special_delete(foo * bar)
{
printf("BYE BYE \n");
delete bar;
}
};
template <> void custom_deletor<foo>(foo * bar)
{
foo::special_delete(bar);
}
int main(int argc, const char * argv[])
{
auto v = make_unique(new foo);
return 0;
}
答案 2 :(得分:0)
在C ++ 17中:
template <auto F>
struct Functor
{
template <typename... Args>
auto operator()(Args&&... args) const { return std::invoke(F, std::forward<Args>(args)...); }
};
允许:
std::unique_ptr<char, Functor<printf>>(new char[50]{ "Hello Template Metaprogramming World!" });
答案 3 :(得分:0)
您还可以使用lambda作为创建删除函子的简写语法。
auto foo_deleter = [](foo *ptr){ destroy_foo(ptr); };
std::unique_ptr<foo, decltype(foo_deleter)> foo_ptr(create_foo(), foo_deleter);
static_assert(sizeof(foo_ptr) == sizeof(void *), "No size overhead");
在C ++ 20中,您可以省略deleter参数,从而更明显地表明它没有存储。
另一个想法是为foo重载std :: default_delete <>:
// In header:
template<>
struct std::default_delete<foo> {
void operator()(foo *f) const { destroy_foo(f); };
};
// Usage:
std::unique_ptr<foo> foo_ptr(create_foo());
static_assert(sizeof(foo_ptr) == sizeof(void *), "No size overhead");
但这可能是一个坏主意,并且使读者感到惊讶。确保重载的std::default_delete
在作用域内,否则它将使用标准的删除操作。此外,您将无法再创建指向正在管理堆内存的foo类型的智能指针。 / p>
答案 4 :(得分:0)
您需要做的是使用 c++11 函数适配器 txt_output.txt
或 std::bind
的工具,将函数转换为函数对象。
这是一个演示。
std::mem_fn
输出:
#include <iostream>
#include <string>
#include <functional>
#include <typeinfo>
int myFun( const std::string& s, int i )
{
std::cout << s << '\n';
return i * i;
}
template<typename Func, typename... TArgs>
decltype(auto) toFunctor( Func f, TArgs&&... args )
{
return std::bind( f, std::forward<TArgs>( args )... );
}
int main()
{
using namespace std::string_literals;
std::cout << "Func\n"s;
std::cout << typeid( myFun( "hello"s, 5 ) ).name() << '\n';
std::cout << "Function object\n"s;
auto f = toFunctor( myFun, "hello"s, 5 );
std::cout << typeid( f ).name() << '\n';
f();
}