【问题标题】:why do we need to capture reference for a reference in lambda?为什么我们需要为 lambda 中的引用捕获引用?
【发布时间】:2016-02-18 17:32:14
【问题描述】:

考虑一下:

class TestLambda {
public:
    std::vector<char> data;
};

void test_lambda(TestLambda& obj) {
    [=]() mutable {
        obj.data.push_back(0x01);
    }();
}

int main() {
    TestLambda tst;
    tst.data.push_back(0x99);
    test_lambda(tst);

    // tst.data is unchanged at this point

    return 0;
}

在致电test_lambda 之后,我期望看到tst.data 的变化,但事实并非如此。要查看更改,我必须创建 lambda,再次传递 obj 的引用,即。 [&amp;obj]().

为什么我们需要这个?我的意思是,又是一个参考?

obj 已经是一个参考。然后,lambda 通过复制捕获obj。那么,lambda 中的obj 本身不是引用吗?为什么?

有人可以解释一下吗?谢谢。

【问题讨论】:

  • 引用是一个别名,所以它只是另一个左值。在几乎所有情况下,它的行为都与它所引用的变量没有什么不同。复制原件或别名的副本,它仍然是副本。

标签: c++ c++11 lambda pass-by-reference


【解决方案1】:

当在赋值的右侧使用时,引用就像一个“正常”变量一样工作。每当您定义按值捕获的 lambda 时,lambda 都拥有外部变量的副本,就好像 lambda 以这些行开头:

auto my_inner_variable = my_outer_reference;
auto my_inner_other_variable = my_outer_other_variable;

如果您希望引用“保持”引用,则必须通过引用捕获它,从而指示编译器发出如下代码:

auto& my_inner_variable = my_outer_reference;
auto& my_inner_other_variable = my_outer_other_variable; // if we instructed to capture everything by reference

【讨论】:

    【解决方案2】:

    根据草案标准§5.1.2/p15 Lambda 表达式 [expr.prim.lambda](Emphasis Mine):

    如果实体被隐式捕获并且捕获默认值为 =,或者如果它通过捕获显式捕获,则通过副本捕获实体 这不是 & 标识符或 & 标识符初始化程序的形式。 对于复制捕获的每个实体,一个未命名的非静态数据成员是 在闭包类型中声明。这些成员的申报顺序 未指定。 此类数据成员的类型是 如果实体不是对 对象,否则引用类型。 [注意:如果捕获 实体是对函数的引用,对应的数据成员是 也是对函数的引用。 — 尾注] 匿名成员 union 不得抄袭。

    因此,在:

    void test_lambda(TestLambda& obj) {
        [=]() mutable {
            obj.data.push_back(0x01);
        }();
    }
    

    obj 是通过副本捕获的,因此您可以正确获得所描述的结果。换句话说,这是标准规定的 [=] 捕获默认行为。

    【讨论】:

    • 如何“隐式”捕获 obj? []() {} 给我“未捕获”错误。 +1。
    • 我认为 OP 理解 [=] 的含义。我认为问题是关于捕获实体的类型:为什么捕获的引用会复制。捕获可以演示为:auto objCopy = obj; 在 lambda 中,应该是 TestLambda objCopy = obj; 还是 TestLambda&amp; objCopy = obj;? @Lodo 的回答即使不是很清楚也能解决这个问题
    • @AndyT 对。我缺乏知识是在做 TestLambda objCopy = obj; 时发生的事情。现在我明白为什么它不再作为参考了。
    • "...or the referenced type else"是这里要强调的一句话
    【解决方案3】:

    您的函数 test_lambda 包含一个嵌套的 lambda 函数。在test_lambda 中,引用obj 在main 中引用tst。然后调用一个按值捕获的匿名 lambda 函数。在 lambda 函数 obj 内部是 test_lambda 内部的 obj 的副本。为什么不简单地写:

    void test_lambda(TestLambda& obj) {
       obj.data.push_back(0x01);
    }
    

    你现在正在做的事情或许可以通过

    来说明
    void test_lambda(TestLambda& obj) {
        [=]() mutable {
            objCopy.data.push_back(0x01);
        }();
    }
    

    objCopy 由捕获 lambda 的值创建。

    【讨论】:

    • 我写了这个 sn-p 来表达我的问题。问题不在于我如何编写函数来更改 obj。
    猜你喜欢
    • 2013-04-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-12-14
    • 2011-01-09
    相关资源
    最近更新 更多