c++ Lambdas并通过引用局部变量捕获:在范围之后访问

我通过引用两个lambda传递我的局部变量.我将这些lambda称为函数范围之外的函数.这是不确定的?

std::pair<std::function<int()>, std::function<int()>> addSome() {
    int a = 0, b = 0;
    return std::make_pair([&a,&b] {
        ++a; ++b;
        return a+b;
        }, [&a, &b] {
            return a;
        });
}

int main() {
    auto f = addSome();
    std::cout << f.first() << " " << f.second();
    return 0;
}

但是,如果不是,则一个lambda中的变化不会反映在其他lambda中.

我是否误解了lambda的背景中的传递?

我正在写变量,它似乎工作正常,输出没有运行时错误

2 0.如果它工作,那么我期望输出2 1.

最佳答案
是的,这会导致未定义的行为. lambdas将引用超出范围的堆栈分配对象. (从技术上讲,据我所知,行为被定义,直到lambdas访问a和/或b.如果你从不调用返回的lambdas,那么就没有UB.)

这是未定义的行为,就像它返回对堆栈分配的本地的引用的未定义行为一样,然后在本地超出范围之后使用该引用,除了在这种情况下它被lambda稍微混淆了一点.

此外,请注意调用lambda的顺序是未指定的 – 编译器可以在f.first()之前自由调用f.second(),因为两者都是同一个完整表达式的一部分.因此,即使我们修复了因使用对已销毁对象的引用而导致的未定义行为,2 0和2 1仍然是此程序的有效输出,并且您获得的取决于编译器决定执行lambdas的顺序.请注意,这不是未定义的行为,因为编译器根本无法执行任何操作,而只是在决定执行某些操作的顺序方面有一些自由.

(请记住,<<在你的main()函数中调用自定义运算符<<函数,并且未指定函数参数的计算顺序.编译器可以自由地发出代码来计算所有函数参数在任何顺序的同一个完整表达式中,约束条件是在调用该函数之前必须计算函数的所有参数.) 要解决第一个问题,请使用std :: shared_ptr创建引用计数对象.按值捕获此共享指针,只要它们(及其任何副本)存在,lambdas将使指向的对象保持活动状态.这个堆分配的对象是我们存储a和b的共享状态的地方. 要解决第二个问题,请在单独的语句中评估每个lambda. 下面是您修复了未定义行为的代码,并且f.first()保证在f.second()之前调用:

std::pair<std::function<int()>, std::function<int()>> addSome() {
    // We store the "a" and "b" ints instead in a shared_ptr containing a pair.
    auto numbers = std::make_shared<std::pair<int, int>>(0, 0);

    // a becomes numbers->first
    // b becomes numbers->second

    // And we capture the shared_ptr by value.
    return std::make_pair(
        [numbers] {
            ++numbers->first;
            ++numbers->second;
            return numbers->first + numbers->second;
        },
        [numbers] {
            return numbers->first;
        }
    );
}

int main() {
    auto f = addSome();
    // We break apart the output into two statements to guarantee that f.first()
    // is evaluated prior to f.second().
    std::cout << f.first();
    std::cout << " " << f.second();
    return 0;
}

(See it run.)

转载注明原文:c++ Lambdas并通过引用局部变量捕获:在范围之后访问 - 代码日志