2

我有一个(希望)关于 lambda 表达式的简单问题:

#include <vector>
#include <algorithm>
//----------------------------------------------------------------
void DoSomething()
//----------------------------------------------------------------
{
  std::vector<int> elements;
  elements.push_back(1);
  elements.push_back(2);

  int ref = 1;
  auto printhit = [=](int iSomeNumber)
  {
    if (ref == iSomeNumber)
    {
      printf("Hit: %d\n", iSomeNumber);
    }
    else
    {
      printf("No Hit: %d\n", iSomeNumber);
    }
  };

  ref = 2;
  std::for_each(elements.begin(), elements.end(), printhit);   
}

现在,我的问题是:当我用 capture [=] 定义 printhit 时,它会打印“Hit: 1”。如果我通过引用 [&] 传递它,它会打印“Hit: 2”。我以某种方式期望,替换是在 for_each 中完成的,因此无论我如何授予对“ref”的访问权限,都会打印“Hit: 2”。

谁能给我解释一下?

谢谢,马库斯

4

3 回答 3

5

捕获发生在您声明 lambda 的位置。就像您要在那时创建一个类对象并传递ref给它的构造函数一样。

您的示例等效于:

class Functor
{
public:
    Functor(int r) :ref(r) {}

    void operator()(int iSomeNumber) const
    {
        if (ref == iSomeNumber)
        {
            printf("Hit: %d\n", iSomeNumber);
        }
        else
        {
            printf("No Hit: %d\n", iSomeNumber);
        }    
    }
private:
    int ref;
};

void DoSomething()
//----------------------------------------------------------------
{
  std::vector<int> elements;
  elements.push_back(1);
  elements.push_back(2);

  int ref = 1;
  Functor printhit(ref);

  ref = 2;
  std::for_each(elements.begin(), elements.end(), printhit);   
}
于 2013-11-15T07:59:06.267 回答
1

我想,C++ 标准的以下部分适用:

5.1.2.14:
如果实体被隐式捕获并且捕获默认值为 =,或者如果使用不包含 & 的捕获显式捕获实体,则通过副本捕获实体。对于复制捕获的每个实体,在闭包类型中声明了一个未命名的非静态数据成员。这些成员的声明顺序是未指定的。如果实体不是对对象的引用,则此类数据成员的类型是相应捕获实体的类型,否则为引用类型。[注:如果捕获的实体是对函数的引用,则对应的数据成员也是对函数的引用。——尾注]
5.1.2.21:
当计算 lambda 表达式时,复制捕获的实体用于直接初始化结果闭包对象的每个相应的非静态数据成员。(对于数组成员,数组元素以下标递增的顺序直接初始化。)这些初始化以声明非静态数据成员的(未指定)顺序执行。[注意:这确保了破坏将按照构造的相反顺序发生。——尾注]
于 2013-11-15T08:09:00.713 回答
1

让它们都以相同的方式运行有什么意义?的重点[=]是支持通过复制而不是通过引用来捕获。

想象一下,如果[=]不可用:如果您知道代码中定义 lambda 的位置的运行时值并希望 lambda 以后使用它,那么该值如何可用于 lambda 代码?虽然DoSomething()通过引用[&]访问它的局部ref变量可能会起作用,但是如果你想让 lambda 的生命周期比DoSomething()包含它的本地范围更长,或者想要更改 的值ref而不影响未来对 lambda 的调用,该怎么办?从概念上讲,您可以让语言禁止所有这些事情(使用 afterref已更改或更改ref或调用 lambda afterref已更改或超出范围),或者程序员可以详细说明长度以放置reflambda 使用的某个地方(例如,在堆上,需要管理释放,或者在一些具有重入和线程安全问题的静态缓冲区中),但为了方便语言提供[=]. 编译器生成的 lambda 有效地负责存储和销毁/释放ref.

于 2013-11-15T08:29:17.917 回答