加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 百科 > 正文

lambda – C 0x闭包的未定义行为:II

发布时间:2020-12-16 10:18:48 所属栏目:百科 来源:网络整理
导读:我发现使用C 0x闭包令人困惑.我的初始 report和 subsequent one产生了比解释更多的混乱.下面我将向您展示麻烦的示例,我希望找出代码中存在未定义行为的原因.代码的所有部分都没有任何警告地通过gcc 4.6.0编译器. 计划1:它的工作原理 #include iostreamint m
我发现使用C 0x闭包令人困惑.我的初始 report和 subsequent one产生了比解释更多的混乱.下面我将向您展示麻烦的示例,我希望找出代码中存在未定义行为的原因.代码的所有部分都没有任何警告地通过gcc 4.6.0编译器.

计划1:它的工作原理

#include <iostream>
int main(){
    auto accumulator = [](int x) {
        return [=](int y) -> int { 
            return x+y;
        }; 
    };
    auto ac=accumulator(1);
    std::cout << ac(1) << " " << ac(1) << " " << ac(1) << " " << std::endl;
    std::cout << ac(1) << " " << ac(1) << " " << ac(1) << " " << std::endl;
    std::cout << ac(1) << " " << ac(1) << " " << ac(1) << " " << std::endl;
}

产量符合预期:

2 2 2

2 2 2

2 2 2

2.第2号方案:关闭,工作正常

#include <iostream>
int main(){
    auto accumulator = [](int x) {
        return [&](int y) -> int { 
            return x+=y;
        }; 
    };
    auto ac=accumulator(1);
    std::cout << ac(1) << " " << ac(1) << " " << ac(1) << " " << std::endl;
    std::cout << ac(1) << " " << ac(1) << " " << ac(1) << " " << std::endl;
    std::cout << ac(1) << " " << ac(1) << " " << ac(1) << " " << std::endl;
}

输出是:

4 3 2

7 6 5

10 9 8

程序3:带有std :: function的程序1,Works Fine

#include <iostream>
#include <functional>     // std::function

int main(){

    typedef std::function<int(int)> fint2int_type;
    typedef std::function<fint2int_type(int)> parent_lambda_type;

    parent_lambda_type accumulator = [](int x) -> fint2int_type{
        return [=](int y) -> int { 
            return x+y;
        }; 
    };

    fint2int_type ac=accumulator(1);

    std::cout << ac(1) << " " << ac(1) << " " << ac(1) << " " << std::endl;
    std::cout << ac(1) << " " << ac(1) << " " << ac(1) << " " << std::endl;
    std::cout << ac(1) << " " << ac(1) << " " << ac(1) << " " << std::endl;
}

输出是:

2 2 2

2 2 2

2 2 2

程序4:带有std :: function,Undefined Behavior的程序2

#include <iostream>
#include <functional>     // std::function

int main(){

    typedef std::function<int(int)> fint2int_type;
    typedef std::function<fint2int_type(int)> parent_lambda_type;

    parent_lambda_type accumulator = [](int x) -> fint2int_type{
        return [&](int y) -> int { 
            return x+=y;
        }; 
    };

    fint2int_type ac=accumulator(1);

    std::cout << ac(1) << " " << ac(1) << " " << ac(1) << " " << std::endl;
    std::cout << ac(1) << " " << ac(1) << " " << ac(1) << " " << std::endl;
    std::cout << ac(1) << " " << ac(1) << " " << ac(1) << " " << std::endl;
}

该计划的第一次运行给出:

4 3 2

4 3 2

12364812 12364811 12364810

第二轮同一程序:

4 3 2

4 3 2

1666060 1666059 1666058

第三个:

4 3 2

4 3 2

2182156 2182155 2182154

我如何使用std :: function打破代码?为什么程序1 – 3运行良好,而程序4正确调用ac(1)三次(!)?为什么4号程序卡在接下来的三个案例中,好像变量x已被值捕获,而不是参考.并且ac(1)的最后三个调用完全不可预测,好像对x的任何引用都将丢失.

解决方法

I hope to find out why there is an
undefined behavior in the code

每次我处理复杂而复杂的lambda时,我觉得首先将其转换为函数 – 对象形式会更容易.因为lambdas只是函数对象的语法糖,并且对于每个lambda,都存在与对应的函数对象的一对一映射.本文解释了如何进行翻译:
http://blogs.msdn.com/b/vcblog/archive/2008/10/28/lambdas-auto-and-static-assert-c-0x-features-in-vc10-part-1.aspx

例如,你的程序没有2:

#include <iostream>
int main(){
    auto accumulator = [](int x) {
        return [&](int y) -> int { 
            return x+=y;
        }; 
    };
    auto ac=accumulator(1);
    std::cout << ac(1) << " " << ac(1) << " " << ac(1) << " " << std::endl;
    std::cout << ac(1) << " " << ac(1) << " " << ac(1) << " " << std::endl;
    std::cout << ac(1) << " " << ac(1) << " " << ac(1) << " " << std::endl;
}

将由编译器大致翻译成这一个:

#include <iostream>

struct InnerAccumulator
{
    int& x;
    InnerAccumulator(int& x):x(x)
    {
    }
    int operator()(int y) const
    {
        return x+=y;
    }
};

struct Accumulator
{
    InnerAccumulator operator()(int x) const
    {
        return InnerAccumulator(x); // constructor
    }
};


int main()
{
    Accumulator accumulator;
    InnerAccumulator ac = accumulator(1);
    std::cout << ac(1) << " " << ac(1) << " " << ac(1) << " " << std::endl;
    std::cout << ac(1) << " " << ac(1) << " " << ac(1) << " " << std::endl;
    std::cout << ac(1) << " " << ac(1) << " " << ac(1) << " " << std::endl;
}

现在,问题变得非常明显:

InnerAccumulator operator()(int x) const
{
   return InnerAccumulator(x); // constructor
}

这里InnerAccumulator的构造函数将引用x,这是一个局部变量,一旦退出operator()范围就会死亡.所以,是的,你只是得到了一个明确的旧的未定义的行为,因为你怀疑.

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读