C++ Lambdas和通过引用捕获局部变量:在作用域之后访问

C++ Lambdas和通过引用捕获局部变量:在作用域之后访问,c++,c++11,lambda,local,C++,C++11,Lambda,Local,我通过引用两个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

我通过引用两个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;
}
std::pair addSome(){
int a=0,b=0;
return std::make_pair([&a,&b]{
++a、 ++b;
返回a+b;
},[&a,&b]{
返回a;
});
}
int main(){
自动f=addSome();

std::cout是的,这会导致未定义的行为。lambda将引用超出范围的堆栈分配对象。(从技术上讲,据我所知,在lambda访问
a
和/或
b
之前,该行为一直被定义。如果从未调用返回的lambda,则没有UB。)

这是未定义的行为,与返回对堆栈分配的本地的引用,然后在本地超出范围后使用该引用的未定义行为相同,只是在这种情况下,lambda会对其进行一点模糊处理

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

(请记住,
秒;
返回编号->第一个+编号->第二个;
},
[数字]{
返回号码->第一;
}
);
}
int main(){
自动f=addSome();
//我们将输出分成两条语句,以保证f.first()
//在f.second()之前求值。

STD:CUT

不幸的是,C++ LAMBDAS可以通过引用捕获,但不能解决“”。

这样做需要在“单元格”中分配捕获的本地变量和垃圾收集或引用计数来进行分配。C++不这样做,不幸的是,这使得C++ LAMBDAS比Lisp、Python或JavaScript等其他语言更不实用和危险。 更具体地说,根据我的经验,您应该不惜一切代价避免通过引用(即使用

[&](…){…}
形式)隐式捕获在本地范围内生存的lambda对象,因为这会导致在维护过程中出现随机故障

始终仔细规划要捕获的内容以及捕获引用的方式和生命周期

当然,使用
[&]
引用捕获所有内容是安全的,如果您所做的只是在同一范围内使用lambda来传递代码,例如将代码传递给
std::sort
之类的算法,而不必在函数外部定义命名的比较器函数或将其定义为本地使用的实用程序函数(我觉得这个用法非常可读,也很好,因为你可以隐式地获取大量上下文,而不需要1.为一些永远不会在其他地方重用的内容创建一个全局名称,2.传递大量上下文或仅为该上下文创建额外的类)


一种有时可以工作的方法是用<强>值> /Stord>Acth> CysDypPTR>代码>到堆分配状态。这基本上是用Python自动执行的(但注意引用循环以避免内存泄漏:Python有一个垃圾收集器,C++没有)。.

当您超出范围时。这将复制您的本地人

void func(void)
{
    auto mylocal = getFromSomeWhere();
    doSearch([=] {
        mylocal->foudnSomthing();
    });
}
在范围内时,最好通过引用使用

MyType func(void)
{
    auto mylocal = getFromSomeWhere();
    return ([&] {
         return mylocal->doOperation();
    })
}

如果闭包及其副本的生存期仅限于封闭的
{},则可以使用
[&]
,否则它是不安全或危险的。由于这种有限生命周期的闭包很常见,避免它作为一般规则似乎有点过头了?@Yakk:我想使用lambdas作为闭包,而不是将代码嵌入算法中的糟糕方法。我将编辑来解释这一点。指出funarg问题并与其他语言。我不认为说人们应该不惜一切代价避免引用捕获是个好建议。在我的经验中,这通常是一个好主意。显然,在lambda执行的整个生命周期中,价值必须保持在范围内。@DanNissenbaum:我说过你应该
MyType func(void)
{
    auto mylocal = getFromSomeWhere();
    return ([&] {
         return mylocal->doOperation();
    })
}