我一直在努力解决lambda表达式的一个问题,这个问题正在危害我的一个项目。我找到了一个解决方案,但我想确切地了解它的工作原理和原因,以及它是否可靠。
#include <iostream>
#include <functional>
#include <unordered_map>
typedef std::function<const int&(const int&)> Callback;
int f(int i, Callback callback) {
if (i <= 2) return 1;
return callback(i-1) + callback(i-2);
}
int main(void) {
std::unordered_map<int, int> values;
Callback callback = [&](const int& i) {
if (values.find(i) == values.end()) {
int v = f(i, callback);
values.emplace(i, v);
}
return values.at(i);
};
std::cout << f(20, callback) << std::endl;
return 0;
}
我知道这是计算第20个斐波纳契数的一种疯狂方法,但它是我能够详细说明的最紧凑的SSCCE。
如果我用g++ -O0
编译上面的代码并执行程序,我得到6765
,这实际上是第20个Fibonacci数。如果我使用-O1
,-O2
或-O3
进行汇编,我会收到262144
,这是垃圾。
如果我使用Valgrind(使用-O0 -g
进行编译)对程序进行概要分析,我会在行Conditional jump or move depends on uninitialised value(s)
上获得std::cout << f(20, callback) << std::endl;
,但堆栈跟踪没有说明任何有用的内容。
我不知道为什么我最终得到了这个:
Callback callback = [&](const int& i) -> const int& {
通过这个小小的修改,一切都按预期编译,任何优化级别,Valgrind报告没有问题。
你能帮我理解发生了什么吗?
答案 0 :(得分:12)
如果没有-> const int&
,则lambda的返回类型为int
。由于Callback
的返回类型为const int&
,callback
最终会返回对用于保存lambda表达式返回值的临时值的引用。未定义的行为。
对于这个简单的例子,简单的解决方法是根本不使用引用(Example at Coliru),但我认为你的真实代码处理的重量级对象比int
更重要。遗憾的是,当您指定一个返回对其返回类型 引用的std::function
的非引用的函数时,未指定std::function
进行警告。
答案 1 :(得分:3)
Casey的answer即将开启。我想补充一些东西。 (很明显,在凯西的好答案之上添加正在成为我的常见行为;-)。)实际上,我的评论是gd1对Casey post的评论。
我猜OP正在使用g++ -std=c++1y
,因为在C ++ 11中,返回的lambda类型设置为void
。实际上,只有当lambda的主体包含单个return
语句时,编译器才会推断出返回的类型。否则,编译器假定此类型为void
。在C ++ 14中,返回类型的自动演绎(不仅对于lambda而且对函数而言)更强大。
正如人们可以看到n3582 - 那些被批准用于C ++ 14并且gcc
的实现是参考的论文 - 陈述
普通汽车永远不会推断出参考,[...]
在OP的示例中,返回的类型为int
(不是const int&
)。我相信可以使用-> decltype(auto)
让自动扣除产生参考。