使用棘手的lambda表达式的奇怪的未定义行为

gd1*_*gd1 11 c++ lambda functional-programming c++11

我一直在努力解决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;

}
Run Code Online (Sandbox Code Playgroud)

我知道这是计算第20个Fibonacci数的一种疯狂方法,但它是我能够详细说明的最紧凑的SSCCE.

如果我编译上面的代码g++ -O0并执行程序,我得到6765,这实际上是第20个斐波纳契数.如果我编译-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& {
Run Code Online (Sandbox Code Playgroud)

通过这个小小的修改,一切都按预期编译任何优化级别,Valgrind报告没有问题.

你能帮我理解发生了什么吗?

Cas*_*sey 12

没有-> const int&lambda的返回类型int.由于返回类型Callbackconst int&,callback最终返回对临时的引用,它用于保存lambda表达式的返回值.未定义的行为.

对于这个简单的例子,简单的解决方法是根本不使用引用(Coliru的例子),但我假设你的真实代码处理的是比重量级更重的对象int.不幸的std::function是,当您指定一个函数返回对其std::function返回类型引用的非引用时,未指定警告.