我想知道编写更高阶函数的主要区别,优点和缺点,作为输入参数,使用std::function
或转发参考,例如template<typename F> void hof(F&& fun);
。
显然,前者比后者更严格,因为它指定了输入可调用对象必须符合的函数类型。
答案 0 :(得分:4)
std::function
通常会产生大量的运行时开销。通过template
参数传递通用可调用对象可以避免std::function
的间接成本,可以让编译器积极优化。
我在the end of this article为lambda递归(Y-combinator vs std::function
)写了一些简单的基准测试。与非多态Y组合器实现相比,std::function
总是生成至少3.5倍的汇编。这是一个不错的示例,它显示了std::function
比template
参数更昂贵的方式。
我建议在gcc.godbolt.org上玩,看看两种技术之间的装配差异。
以下是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
}
如果STDFN
未定义,则生成的程序集为:
main:
mov DWORD PTR state[rip], 10
xor eax, eax
ret
state:
.zero 4
STDFN
定义,生成的程序集长48行。
答案 1 :(得分:3)
std::function
有很多专业人士,但它也有一系列缺点,你必须考虑到这一点。
举个例子:
可调用对象应该是可复制的 换句话说,这编译:
#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>()](){});
}
但这不是:
#include <functional>
#include <utility>
#include <memory>
void func(std::function<void(void)> f) { f(); }
int main() {
func([ptr = std::make_unique<int>()](){});
}
尽管(强调我的):
鼓励实现以避免为小型可调用对象使用动态分配的内存,例如,f的目标是仅包含对象的指针或引用的对象,以及成员函数指针。
您无法保证在可以避免的情况下您不会对动态存储进行分配,并且您在所有其他情况下都可以确定分配。
构建std::function
时,构造函数可能会抛出bad_alloc
。
......可能我们可以继续,但它不值得,你明白了。
std::function
是一个非常有用的工具,但是当你需要时你应该使用它们。
例如,如果您打算在某处存储功能,可能最终会使用std::function
。另一方面,如果您计划接受可调用对象并在运行中调用它,可能您不会使用std::function
。
这些仅仅是几个例子,不幸的是,黄金法则并不存在。