我看到很多人建议不要使用std::function<>
,因为它是一个较重量级的机制。有人能否解释一下这是为什么?
std::function
是一种类型擦除的类。
它会保留以下内容,但擦除其余所有内容:
可能还包括:
这会导致一些开销。一个典型的高质量的std::function
将具有小对象优化(例如小字符串优化),当使用的内存量较小时,避免了堆分配。
函数指针也可以适应其中。
然而,仍然存在一些开销。如果使用兼容的函数指针初始化std::function
,而不是直接调用相关的函数指针,则会进行虚函数表查找或调用其他函数,然后再调用函数指针。
在vtable的实现中,这是可能会引起缓存未命中、指令缓存未命中,然后又一次指令缓存未命中。对于函数指针,指针可能存储在本地,并且直接调用它,只引起一次指令缓存未命中。
此外,在实践中编译器对函数指针的理解比std::function
更好:一些编译器可以在内联或整个程序优化期间判断出指针是常量值。我从未看到过一个能够处理std::function
的。
对于较大的对象(例如在某个实现中大于sizeof(std::string)
),std::function
还会进行堆分配。这是另一种开销。对于函数指针和引用包装器,标准保证采用SOO。
直接存储lambda而不将其存储在std::function
中甚至比函数指针更好:在这种情况下,正在运行的代码隐含在lambda类型中。这使得代码可以轻松确定它被调用时会发生什么,并且易于编译器进行内联。
只有在需要时才进行类型擦除。
reference_wrapper
)都保证使用小对象优化。 - T.C.std::function
是否提到了对函数的引用?我在_cppreference_上没有看到任何相关信息(显然这并不是决定性的)。快速测试表明它可以从一个函数引用构造,并且可以正常工作,但我不知道这是否证明了什么。该引用是否只转换为指针?无论如何,我都希望能在某个地方提到引用的情况。 - underscore_d在内部,std::function
通常使用类型擦除(一种实现方式的简化解释可以在这里找到)。存储您的函数对象在 std::function
对象内可能会涉及堆分配的成本。调用您的函数对象的成本通常是通过指针间接引用再加上虚函数调用。此外,虽然编译器正在改进,但虚函数调用通常抑制了您的函数的内联。
尽管如此,我建议使用 std::function
,除非您通过测量知道成本太高(通常在无法承受堆分配、需要非常低延迟的地方多次调用您的函数等情况下),因为编写直截了当的代码比过早地进行优化更好。
根据实现方式不同,std::function
由于使用类型擦除会增加一些开销。还有其他实现方式,例如 Don Clugston 的快速委托,在 C++11 中实现 这里。请注意,它使用 UB 来创建最快的委托,但仍然非常可移植。
auto l = [](){};
<algorithm>
或自己的等效模板进行类型抹除,因为没有必要让异质函数对象类型共存。这并不是这样。
简单来说,除非你对程序进行了分析并证明它太重,否则它并不会太重。显然你没有这样做(否则你就会知道这个问题的答案),所以我们可以安全地得出结论,它实际上并不重。
在得出它运行速度太慢的结论之前,你应该始终进行分析。