2017-02-09 72 views
5

我想实现用C++这块高阶功能的蟒蛇:不同的结果C++ 14

def add1(x): 
    def helper(): 
     nonlocal x 
     x += 1 
     return x 
    return helper 

这里有三个版本我创建:

#include <iostream> 
#include <functional> 

using namespace std; 

function<int(void)> add1_v1(int x) { 
    function<int(void)> g = [&x]() {return ++x;}; 
    return g; 
} 

auto add1_v2(int x) { 
    function<int(void)> g = [&x]() {return ++x;}; 
    return g; 
} 

auto add1_v3(int x) { 
    auto g = [&x]() {return ++x;}; 
    return g; 
} 

int main() { 
    auto a = add1_v1(100); 
    auto b = add1_v2(100); 
    auto c = add1_v3(100); 
    for(int i = 0; i < 3; ++i) { 
     cout << a() << endl; 
    } 
    cout << "-------------------------------------------" << endl; 
    for(int i = 0; i < 3; ++i) { 
     cout << b() << endl; 
    } 
    cout << "-------------------------------------------" << endl; 
    for(int i = 0; i < 3; ++i) { 
     cout << c() << endl; 
    } 
    return 0; 
} 

输出为:

101 
102 
103 
------------------------------------------- 
4239465 
4239466 
4239467 
------------------------------------------- 
4201325 
4201325 
4201325 

只有add1_v1匹配我想要的东西。任何人都可以解释我的理由吗?

+0

什么是“不工作”是什么意思? – Brian

+0

@Brian修改了这个问题。 –

+0

他们都错了。 'add1_v1'很幸运。 – immibis

回答

7

原因是这是未定义的行为。

内部lambda通过引用捕获x

问题是,只要add()返回,它的参数就会被销毁,并且返回的lambda具有对被销毁对象的悬挂引用。

该lambda必须按值捕获x;什么在我看来你真的想在这里做的是一个mutable lambda

auto add(int x) { 
    function<int(void)> g = [x]() mutable {return ++x;}; 
    return g; 
} 

注意,这种方法带有一定的影响,当涉及到随后复制返回拉姆达;但只要返回的lambda仍然“在一个地方”,在其整个剩余的生命周期内,所产生的语义可能就是您所期望的。

+1

'auto long_live_x = make_shared (x); return [=](){return ++ * long_live_x;};'也是可能的,有不同的折衷。 – ephemient

4

他们都是病态的,因为你在拉姆达参考捕捉x,但x是一个局部变量,会被摧毁时摆脱功能add,则引用变得吊着,提领就可以了后者会导致UB,这意味着任何事情都是可能的;即使是第一个案件似乎工作正常。

0

不是一个答案,似乎功能所有版本的效果很好,如果你更改参数签名add1_v *(INT & & X)