C++0x Lambda开销

28

在C++0x(VS2010下)使用lambda表达式是否会产生任何开销?
我知道使用函数对象会产生开销,但我指的是传递给STL算法的表达式。编译器是否优化这个表达式,消除看起来像函数调用的东西?我开始非常喜欢lambda表达式,但我有点担心速度惩罚。

提前致谢!


7
你为什么确定使用函数对象(functor)会产生额外的开销?它们也可以进行优化!唯一的方法是启用完全的优化构建,并查看汇编代码,以此来判断。 - EFraim
当我说函数对象时,我指的是像boost::function这样的东西,我的错。我知道经典函数对象可以内联,我想知道lambda也能否实现此功能。 - Gratian Lup
啊,好的,那些确实会有一些开销。但是Lambda表达式不会(除非你将它们包装在一个std::function对象中(不需要使用boost,因为它已经被0x标准采纳了)。 - jalf
2个回答

50

你“知道”函数对象会产生开销?也许你应该重新检查一下事实。 :)

使用STL算法与函数对象相比,通常不会产生任何开销,与手写循环相比也是如此。一个天真的编译器将不得不在函数对象上反复调用operator(),但这是微不足道的并且可以轻松内联化,因此实际上开销为零。

lambda表达式只是函数对象的语法糖。编译器将代码转换为函数对象,因此它也没有额外的开销。


5
根据实际情况来看,这是错误的。在VS2010中,添加许多std::function实例会显著增加可执行文件的大小。我刚刚进行了这个测试。 - shoosh
8
那又怎样呢?添加大量的 std::string 实例也会带来很大的开销。但问题和我的回答并不是关于这个的。它们涉及函数对象和 lambda。而 std::function 则不是其中之一。 - jalf
9
仅作详细说明,std::function 是一个包装类,对所有可调用对象进行抽象,无论是函数指针还是仿函数。使用它并不免费。但是,如果您避免使用包装器,直接使用一个仿函数或一个 lambda 表达式,则没有任何额外开销。 - jalf
@jalf 对不起我的无知..但是难道你不必使用std::function来声明一个接受lambda作为参数的函数吗? - Tomas Andrle
1
@TomA:并非完全如此。一个接受某种类型 T 的函数模板可以将一个lambda作为参数,当然,对于非模板函数,你必须指定实际的参数类型,如果类型是一个lambda,那么你不能这样做,因此在这种情况下,你必须将其包装在std::function或类似物中。 - jalf
1
正如jalf所说,没有必要使用std :: function包装lambda。 std :: function在运行时确实会产生抽象开销,但lambda不会。假设您的函数对象已经优化(ctor和operator()内联),则与之相比,lambda表达式确实没有任何开销。现在,无论您是直接使用lambda还是函数对象,如果您想通过std :: function存储它们并在运行时调用它们,则都会有开销。 - stinky472

20

在幕后,

void f(char delim)
{
  std::for_each( seq.begin()
               , seq.end()
               , [=](const T& obj){std::cout << obj << delim;} );
}

大约翻译为

class __local_class_name {
  char __delim;
public:
  __local_class_name(char delim) : __delim(delim) {}
  void operator()(const T& obj) {std::cout << obj << __delim;}
};

void f(char delim)
{
  std::for_each( seq.begin()
               , seq.end()
               , __local_class_name(delim) );
}

与所有函数对象一样,开销非常小,因为可以轻松地将调用内联。


4
他使用了 [=] 这个符号,表示所有本地对象都将以值的方式被捕获。 - Dennis Zickefoose
1
@Dennis:只有jalf添加了“=”,所以@Dario是正确的。:( @jalf:谢谢。我的借口是我还没有时间去尝试这个,所以这一切都是从传闻中打出来的。 - sbi
1
是的,我应该留下一条注释说明我添加了它。:) 但是,[=] 按值捕获所有内容。我们也可以使用 [delim] 来捕获特定的变量。顺便说一句,我点赞了你的答案。 :) - jalf
__local_class_name 将成为函数 f 作用域内的本地类。C++0x 允许将局部类实例用作函数模板的参数(它们在当前标准下不允许)。值得注意的是,对于每个 lambda,编译器都会生成类名,您无法自己命名 lambda 的类型。 - snk_kid
你会如何进行内联调用?似乎无论我看哪里,人们都说“调用可以轻松地进行内联”,但我不知道怎么做...我的意思是,我不能在operator()的定义之前写入inline,或者我只是写了[](){return stuff;}这一部分,我该如何请求内联? - Born2Smile
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接