使用C ++ 0x关闭的未定义行为:II [英] Undefined Behavior with the C++0x Closure: 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
$ b2 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
$ b1666060 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屋!