使用C ++ 0x关闭的未定义行为:II [英] Undefined Behavior with the C++0x Closure: II

查看:218
本文介绍了使用C ++ 0x关闭的未定义行为:II的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我发现使用C ++ 0x闭合复杂。我的初始报告,并且后续一个,产生的混乱比解释。下面我将给你一些麻烦的例子,我希望找出为什么在代码中有一个未定义的行为。所有代码段都通过gcc 4.6.0编译器,没有任何警告。



程序号1:它工作



  #include< iostream> 
int main(){
auto accumulator = [](int x){
return [=](int y) - > int {
return x + y;
};
};
auto ac = accum(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

$ b

2 2 2



2。程序2:关闭,工作正常



  #include< iostream> 
int main(){
auto accumulator = [](int x){
return [&](int y) int {
return x + = y;
};
};
auto ac = accum(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

10 9 8



计划3:带std :: function的程序1,工作精细



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

int main(){

typedef std :: function< int(int)> fint2int_type;
typedef std :: function< fint2int_type(int)>父_lambda_type;

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

fint2int_type ac = accum(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



计划4:带std ::函数的程序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 {
returns x + = y;
};
};

fint2int_type ac = accum(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


12364812 12364811 12364810



同一程式的第二次执行:



4 3 2

$ b

1666060 1666059 1666058



第三个:



strong> 4 3 2


$ b

strong> 2182156 2182155 2182154



我如何使用std :: function打破代码?为什么程序1 - 3工作得很好,程序4在调用ac(1)三次(!)时是正确的?为什么程序4在接下来的三种情况下停滞,就好像变量x是通过值捕获的,而不是引用。并且ac(1)的最后三个调用是完全不可预测的,如果对x的任何引用将丢失。

解决方案


我希望找出为什么在代码中有一个
未定义的行为


每次处理复杂而复杂的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



例如,你的程序no 2: / p>

  #include< iostream> 
int main(){
auto accumulator = [](int x){
return [&](int y) int {
return x + = y;
};
};
auto ac = accum(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()
{
累加器累加器;
InnerAccumulator ac = accum(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,a局部变量,只要你退出operator()范围就会死。所以是的,你只是得到一个平原好老未定义的行为,你怀疑。


I find the use of the C++0x closure perplexing. My initial report, and the subsequent one, have generated more confusion than explanations. Below I will show you troublesome examples, and I hope to find out why there is an undefined behavior in the code. All the pieces of the code pass the gcc 4.6.0 compiler without any warning.

Program No. 1: It Works

#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;
}

The output meets the expectations:

2 2 2

2 2 2

2 2 2

2. Program No. 2: Closure, Works Fine

#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;
}

The output is:

4 3 2

7 6 5

10 9 8

Program 3: Program No. 1 with std::function, 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;
}   

The output is:

2 2 2

2 2 2

2 2 2

Program 4: Program No. 2 with std::function, Undefined Behavior

#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;
}

The first run of the program gives:

4 3 2

4 3 2

12364812 12364811 12364810

The second run of the same program:

4 3 2

4 3 2

1666060 1666059 1666058

The third one:

4 3 2

4 3 2

2182156 2182155 2182154

How does my use of the std::function break the code? why do Programs No.1 - 3 work well, and Program No. 4 is correct when calling ac(1) thrice(!)? Why does Program No. 4 get stuck on the next three cases as if the variable x had been captured by value, not reference. And the last three calls of ac(1) are totally unpredictable as if any reference to x would be lost.

解决方案

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

Every time I deal with complex and intricated lambda, I feel it more easier to do first the translation into function-object form. Because lambdas are just syntactic sugar for function-object and for each lambda there is a one-to-one mapping with a corresponding function-object. This article explain really well how to do the translation : http://blogs.msdn.com/b/vcblog/archive/2008/10/28/lambdas-auto-and-static-assert-c-0x-features-in-vc10-part-1.aspx

So for example, your program no 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;
}

would be approximately translate by the compiler into this one :

#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;
}

And now, the problem become quite obvious :

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

Here the constructor of InnerAccumulator will take a reference to x, a local variable which will die as soon as you exit the operator() scope. So yes, you just get a plain good old undefined behavior as you suspected.

这篇关于使用C ++ 0x关闭的未定义行为:II的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆