简体   繁体   English

使用std :: function或转发引用作为高阶函数的通用可调用对象输入参数?

[英]using a std::function or a forwarding reference as general-purpose callable object input parameter of a higher-order function?

I was wondering about the main differences, pros and cons in writing a higher order function taking, as input parameter, a std::function or a forwarding reference , eg template<typename F> void hof(F&& fun); 我想知道编写高阶函数的主要区别,优点和缺点,作为输入参数, std::function转发引用 ,例如template<typename F> void hof(F&& fun); . Obviously, the former is more strict than the latter in that it specifies the function type that the input callable object has to conform to. 显然,前者比后者更严格 ,因为它指定了输入可调用对象必须符合的函数类型

std::function often has a significant run-time overhead. std::function通常具有显着的运行时开销。 Passing a generic callable object through a template parameter avoids std::function 's indirection costs and allows the compiler to aggressively optimize . 通过template参数传递通用可调用对象可以避免std::function的间接成本,并允许编译器积极地进行优化

I wrote some simple benchmarks for lambda recursion (Y-combinator vs std::function ) at the end of this article . 我在本文末尾为lambda递归(Y-combinator vs std::function编写了一些简单的基准测试。 std::function always generates at least 3.5x times more assembly than a non-polymorphic Y-combinator implementation. 与非多态Y组合器实现相比, std::function总是生成至少3.5倍的程序集。 This is a decent example that shows how std::function can be more expensive than a template parameter. 这是一个很好的例子 ,它显示了std::function如何比template参数更昂贵。

I suggest playing around on gcc.godbolt.org to see assembly differences between the two techniques. 我建议在gcc.godbolt.org ,看看两种技术之间的装配差异。


Here's an example I just came up with : 这是我刚才提出的一个例子:

#if defined(STDFN)
void pass_by_stdfn(std::function<void()> f)
{
    f();
}
#else
template <typename TF>
void pass_by_template(TF&& f)
{
    f();
}
#endif

volatile int state = 0;

int main()
{
#if defined(STDFN)
   pass_by_stdfn([i = 10]{ state = i; });
#else
   pass_by_template([i = 10]{ state = i; });  
#endif
}

With STDFN not defined , the generated assembly is: STDFN 没有定义 ,生成的汇编是:

main:
        mov     DWORD PTR state[rip], 10
        xor     eax, eax
        ret
state:
        .zero   4

With STDFN defined , the generated assembly is 48 lines long. 定义STDFN ,生成的程序集长度为48行。

std::function has a lot of pros, but it has also a series of cons that you must take into account. std::function有很多专业人士,但它也有一系列缺点,你必须考虑到。
As an example: 举个例子:

  • The callable object should be copy constructible. 可调用对象应该是可复制构造的。
    In other terms, this compiles: 换句话说,这编译:

     #include <functional> #include <utility> #include <memory> template<typename F> void func(F &&f) { std::forward<F>(f)(); } int main() { func([ptr = std::make_unique<int>()](){}); } 

    But this does not: 但这不是:

     #include <functional> #include <utility> #include <memory> void func(std::function<void(void)> f) { f(); } int main() { func([ptr = std::make_unique<int>()](){}); } 
  • Even though (emphasis mine): 即使(强调我的):

    Implementations are encouraged to avoid the use of dynamically allocated memory for small callable objects, for example, where f's target is an object holding only a pointer or reference to an object and a member function pointer. 鼓励实现避免为小的可调用对象使用动态分配的内存,例如,f的目标是仅包含对象的指针或引用的对象以及成员函数指针。

    You have no guarantees that you won't have allocations on the dynamic storage when they could be avoided, and you'll have allocations for sure in all the other cases. 您无法保证在可以避免的情况下不会对动态存储进行分配,并且您将在所有其他情况下确保分配。

  • When you construct a std::function , the constructor may throw a bad_alloc . 构造std::function ,构造函数可能会抛出bad_alloc

  • ... Probably we can continue, but it doesn't worth it, you got the point. ......可能我们可以继续,但它不值得,你明白了。

std::function s are in incredible useful tool, but you should use them when you need them. std::function s是一个非常有用的工具,但你应该在需要时使用它们。
As an example, if you plan to store your function somewhere, likely you will end up using a std::function . 例如,如果您计划将函数存储在某个位置,可能最终会使用std::function On the other side, if you plan to accept a callable object and invoke it on the fly, probably you won't use a std::function . 另一方面,如果您计划接受可调用对象并在运行中调用它,可能您不会使用std::function
These are only a few examples, a golden rule doesn't exist unfortunately. 这些仅仅是几个例子,不幸的是,黄金法则不存在。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM