C ++模板将函数转换为函数对象类型

时间:2014-03-21 07:56:56

标签: c++ templates c++11 template-meta-programming

我尝试将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()的类,它只是将所有参数转发给函数,并返回结果。

5 个答案:

答案 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.txtstd::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();
}