【问题标题】:Understanding C++0x lambda captures了解 C++0x lambda 捕获
【发布时间】:2011-07-25 02:51:35
【问题描述】:

在最近的一份 C++0x 草案 (n3225.pdf) 中,我们可以找到 5.1.2/10:

使用非限定名称查找 (3.4.1) 的常用规则查找捕获列表中的标识符;每个这样的查找都应该找到一个在本地 lambda 表达式的到达范围内声明的具有自动存储持续时间的变量。如果实体(即变量或 this)出现在 lambda 表达式的捕获列表中,则称其被显式捕获。

似乎对我来说相当限制。例如,在我看来,似乎以下事情是不允许的:

int global;

struct s {
    int x;
    void memfun() {
        [x,global]{};
    }
};

因为x 不一定是具有自动存储功能的变量,global 也不一定。请注意,此捕获子句的目的是让 lambda 对象存储xglobal副本,以防它们在稍后阶段发生更改。我已经知道另一种选择:

int global;

struct s {
    int x;
    void memfun() {
        int copyx = x;
        int copyglobal = global;
        [copyx,copyglobal]{};
    }
};

但这归结为额外的副本和额外的样板,只是为了将 xglobal 捕获为副本。

另外,我在最新的草稿中找不到任何关于如果我们在捕获子句中命名本地引用会发生什么的结论:

int main() {
    int  i = 0;
    int &r = i;
    assert([r]{return &r;}() != &i);
}

lambda 对象是“复制引用”还是“复制 int”?如果它通过副本捕获引用的对象,这可以为我们节省先前解决方法中的额外副本。

GCC 显然支持所有这些示例,并在最后一种情况下存储 int 的副本(这是可取的,恕我直言)。但我想知道这实际上是根据 C++0x 草案的预期行为,还是只是编译器扩展,分别是实现错误。

编辑:

templatetypedef 指出 5.1.2/14 解释了在捕获子句中命名引用时会发生什么。据我所知,这允许我们对第一个示例使用以下解决方法:

int global;

struct s {
    int x;
    void memfun() {
        auto& cx = x;
        auto& cglob = global;
        [cx,cglob]{};
    }
};

蒂亚, 推销

【问题讨论】:

  • 注意:C++14 扩展了可能的捕获子句集

标签: lambda c++11


【解决方案1】:

从您发布的内容看来,您的第一个示例似乎是非法的,因为两个捕获的变量都没有自动持续时间。 但是,您可以轻松解决此问题。捕获数据成员,直接捕获即可,全局不需要捕获,直接引用即可。

编辑:正如您所指出的,这不会创建您要捕获的值的本地副本。 要在制作副本时捕获这些变量,您可以捕获它,然后在 lambda 内显式创建数据成员的本地副本。

至于关于捕获引用的第二个问题,第 5.1.2/14 节说,通过复制捕获引用类型的变量将创建引用值的副本,而不是创建引用的副本。因此,lambda 将拥有自己的引用创建时引用的值的副本。

【讨论】:

  • 在捕获子句中命名xglobal 的目的是让lambda 对象存储一个copy。如果::globals::x 更改它们的值,这可能是可取的。所以,不,捕获 this 不会导致预期的行为。
  • 你是对的;我刚刚更新了我的答案。感谢您指出这一点!
  • 感谢您指出 5.1.2/14。如果我们有兴趣通过复制捕获其他内容,看起来我们可以引入对非自动变量的本地引用作为解决方法。
  • 在 lambda 内部复制与在 lambda 创建时复制仍然不同。并不是说很难做到,只是不能用你的提案“解决方案”。
猜你喜欢
  • 2011-02-19
  • 1970-01-01
  • 2011-09-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多