更新时间:2023-11-10 19:25:52
我希望找出为什么在代码中有一个
未定义的行为
每次处理复杂而复杂的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.
#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
#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
#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
#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.