使用std :: bind与lambdas绑定。他们有多贵?

时间:2013-10-09 06:43:48

标签: c++11 lambda stdbind

我正在玩bind,我在想,lambda是否像函数指针一样昂贵?

我的意思是,正如我理解lambdas,它们是仿函数的语法糖,而bind类似。但是,如果你这样做:

#include<functional>
#include<iostream>

void fn2(int a, int b)
{
  std::cout << a << ", " << b << std::endl;
}

void fn1(int a, int b)
{
  //auto bound = std::bind(fn2, a, b);
  //static auto bound = std::bind(fn2, a, b);
  //auto bound = [&]{ fn2(a, b); };
  static auto bound = [&]{ fn2(a, b); };
  bound();
}

int main()
{
  fn1(3, 4);
  fn1(1, 2);
  return 0;
}

现在,如果我要使用第一个auto bound = std::bind(fn2, a, b);,我会得到

3,4

的输出

1,2

,第二个我得
3,4

3,4

。第3和第4个输出就像第1个一样。

现在我明白为什么第一和第二个工作方式,它们在函数调用开始时被初始化(静态的,只有第一次被调用)。然而,3和4似乎有编译器魔法在生成的仿函数没有真正创建对封闭范围的变量的引用,但实际上是锁定到符号,无论它是否仅在第一次初始化或每一次。

有人可以澄清这里发生的事情吗?

修改:我缺少的是使用static auto bound = std::bind(fn2, std::ref(a), std::ref(b));将其作为第4个选项。

2 个答案:

答案 0 :(得分:2)

您有以下代码:

static auto bound = [&]{ fn2(a, b); };

分配仅在您第一次调用此函数时完成,因为它是static。所以实际上只召唤一次。在编写lambda时编译器会创建闭包,因此捕获了对第一次调用fn1的a和b的引用。这是非常危险的。它可能导致悬挂引用。我很惊讶它没有崩溃,因为你正在通过值传递的函数参数进行闭包 - 到局部变量。

我推荐这篇关于lambdas的优秀文章:http://www.cprogramming.com/c++11/c++11-lambda-closures.html

答案 1 :(得分:0)

作为一般规则,当你的闭包在当前范围结束时消失时,只使用[&] lambdas。

如果它将超过当前范围,并且需要 by-reference,则显式捕获要捕获的内容,或者创建指向要捕获的内容的本地指针,按价值捕捉它们。

在您的情况下,static lambda代码充满了未定义的行为,因为您[&]在第一次调用中捕获ab,然后在第二次电话。

理论上,编译器可以重写代码以按值而不是通过引用捕获ab,然后每次都调用它,因为该实现与您编写的实现之间的唯一区别当行为未定义时发生,结果会更快。

通过完全忽略static可以完成更高效的工作,因为在您第一次调用范围后离开范围时,static对象的整个状态未定义,并且构造没有可见副作用。

要修复lambda的问题,请使用[=][a,b]来引入lambda,它将按值捕获ab。当我期望lambda持续比当前块更长时,我更喜欢在lambdas上显式捕获状态。