【问题标题】:Binding using std::bind vs lambdas. How expensive are they?使用 std::bind 与 lambda 进行绑定。它们有多贵?
【发布时间】:2013-10-09 06:43:48
【问题描述】:

我在玩 bind,我在想,lambda 和函数指针一样贵吗?

我的意思是,据我了解 lambda,它们是函子的语法糖,并且 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);,我会得到 ​​p> 的输出

3、4

1, 2

,第二个我得到
3, 4

3、4

。第三个和第四个我得到像第一个一样的输出。

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

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

编辑:我缺少的是使用static auto bound = std::bind(fn2, std::ref(a), std::ref(b)); 让它作为第四个选项。

【问题讨论】:

  • @JoachimPileborg 不同的结果是 ab 将无法用于 lambda 函数。这很明显。你想给我看什么?
  • 该死,我的意思是:尝试按值而不是按引用捕获,例如[=]{ ... }。这就是你使用std::bind 时所做的事情,它按值“捕获”变量。
  • @JoachimPileborg 啊,对。如果我在ab 周围使用std::ref,则第二个像第四个一样工作。谢谢。

标签: c++11 lambda stdbind


【解决方案1】:

你有这个代码:

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

分配仅在您第一次调用此函数时完成,因为它是static。所以实际上它只被调用一次。当您制作 lambda 时,编译器会创建闭包,因此会捕获从第一次调用 fn1 时对 a 和 b 的引用。这是非常危险的。它可能会导致悬空引用。我很惊讶它没有崩溃,因为您正在从按值传递的函数参数关闭到局部变量。

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

【讨论】:

  • 这绝对会导致悬空引用。它们绑定到对象ab,一旦函数第一次返回,它们就不再存在。
  • 这是我的想法,没有任何意义。我在想某种编译器魔法正在发生。不,这只是随机的魔法。 :) 它可能只是因为我一个接一个地从同一个函数调用。我希望使用这种方法制作一种“向量表”,但这太昂贵了,因为每次输入函数时都必须生成仿函数表(使用绑定或 lambda)。在这种情况下,我将只使用 reg 函数或成员函数指针。
【解决方案2】:

作为一般规则,仅当您的闭包将在当前范围结束时消失时才使用 [&amp;] lambdas。

如果它要超过当前范围,并且您需要通过引用,请显式捕获您要捕获的内容,或者创建指向您要捕获的内容的本地指针并按价值捕获它们。

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

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

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

要解决您的 lambda 问题,请使用 [=][a,b] 引入 lambda,它会按值捕获 ab。当我希望 lambda 比当前块持续更长时间时,我更喜欢在 lambda 上显式捕获状态。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-07-14
    • 1970-01-01
    • 2011-09-30
    • 1970-01-01
    相关资源
    最近更新 更多