【发布时间】:2020-12-15 05:53:52
【问题描述】:
这篇文章声明使用 C++ Lambda 是“便宜的”: Lambdas aren't magic - part 2
他们演示了如何将 lambdas 传递给现有的 std 函数/模板。 一篇文章演示了如何在不使用 std::function 的情况下使用“auto”作为函数的返回类型来返回 lambda。
我看到的任何文章都没有演示如何制作自己的函数,尤其是。类成员函数,它采用 lambda 或更多,不使用 std::function。
所以这个“lambda 很便宜”的大胆声明 - 在现实世界的场景中真的是真的吗?
作为参考:对于我来说,“便宜”是为了解决这个问题:在具有几百 KB 内存和两位数 MHz 速度的嵌入式裸机项目上非常有用。 (我一直在该领域使用 C++ 的一个健全的子集,并且正在寻找我可以使用的其他东西)
据我所见,std::function 并不便宜。一方面,作为 std::function 传递的 Lambda 显然无法再进行内联优化。 但更糟糕的是,一个 std::function 是 32 字节大。 同样显然,如果捕获的内容超出了范围,是否可以使用动态分配? 这一切听起来都是个坏消息。
因此,当我在寻找在没有 std::function 的情况下使用 lambda 的方法时,只找到了一个返回 auto 的示例,我尝试了这个: 我制作了一个非常简单的类,它在成员函数中使用“auto”作为参数类型,编译器似乎对此很满意(尽管就预期的仿函数参数而言,它不像 std::function 那样“自我记录”代码)。
struct FuncyClass
{ unsigned func(auto fnx)
{ return 2 * fnx(7);
}
};
int main()
{ FuncyClass fc;
auto result = fc.func( [](auto x){return x*3;} );
printf("Result: %u\n", result);
return 0;
}
// Output: "Result: 42"
但我有一种感觉,当它被更复杂的场景使用时,这个非常简单的场景并没有向我展示可能出现的编译器错误泛滥。 我不太了解这种语法在幕后发生了什么,编译器在确定时会做什么,从仿函数的使用,期望什么参数和返回类型,以及这个带有自动参数的函数是如何在幕后实现的.
that,即使用自动类型化的 args,真的是让你的类成员函数 lambda 可定制的明智方法吗? 然后它看起来“便宜”,因为在我的测试中,使用 auto 而不是 std::function 时可执行文件要小得多。
当然,它仍然是有限的: 如果不使用 std::function (或类似类型的 DIY 包装器),类就无法保留 lambda,对吗? 是否有可能防止动态分配发生,例如当出现需要 std::function 分配内存的情况时使其成为编译时错误?
【问题讨论】:
-
Lambda 本身很便宜。
std::function不是,但您不需要使用它。非捕获 lambda 可以转换为函数指针,捕获 lambda 可以作为模板传递。 -
std::function很昂贵,因为它使用类型擦除。一个 lambda 只是一个简写函子,相比之下那些是“便宜的”。您可以将您的类设为模板并为其指定 lambda 类型以避免使用std::function。 -
"不需要使用 std::function" - 是的,但不幸的是,正如我读到的 10 篇文章/博客文章中显示的那样,这是“这样做的方法” - 所以我的印象是这是在您自己的类中有效地使用 lambda 的方法。
-
几乎所有采用函数/函子参数的标准库函数(例如
std::sort等)都无需使用std::function。他们使用模板。 -
这就是权衡。如果您知道类型,则可以进行大量优化,但由于您必须处理类型,因此会使代码更加复杂。如果你去“无类型”(
std::function),那么代码更容易使用,但你会付出性能损失。