如果返回一个向量项,那么lambda表达式的返回类型是什么?

Jin*_* Li 27 c++ lambda c++11 std-function return-type-deduction

请考虑以下代码段:

#include <iostream>
#include <vector>
#include <functional>

int main() 
{
    std::vector<int>v = {0,1,2,3,4,5,6};
    std::function<const int&(int)> f = [&v](int i) { return v[i];}; 
    std::function<const int&(int)> g = [&v](int i) -> const int& { return v[i];};

    std::cout << f(3) << ' ' << g(3) << std::endl;
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

我期待相同的结果:in f,v由const引用传递,所以v[i]应该有const int&类型.

但是,我得到了结果

 0 3
Run Code Online (Sandbox Code Playgroud)

如果我不使用std :: function,一切都很好:

#include <iostream>
#include <vector>
#include <functional>

int main() 
{
    std::vector<int>v = {0,1,2,3,4,5,6};
    auto f = [&v](int i) { return v[i];}; 
    auto g = [&v](int i) -> const int& { return v[i];};

    std::cout << f(3) << ' ' << g(3) << std::endl;
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

输出:

3 3
Run Code Online (Sandbox Code Playgroud)

因此,我想知道:

  1. 在第二个片段中,lambda表达式的返回类型是f什么?是f一样的g吗?

  2. 在第一个片段中,std::function f构建时发生了什么,导致错误?

T.C*_*.C. 19

lambda的auto返回类型使用返回类型推导规则,它剥离了引用性.(最初它使用了一组略有不同的规则,这些规则基于左值到右值的转换(也删除了引用),但是由DR改变.)

因此,[&v](int i) { return v[i];};回报int.结果,在std::function<const int&(int)> f = [&v](int i) { return v[i];};调用中f()返回一个悬空引用.绑定对临时的引用会延长临时的生命周期,但是在这种情况下绑定发生在内部std::function机器的深处,所以到时间f()返回时,临时就已经消失了.

g(3)很好,因为const int &返回直接绑定到vector元素v[i],因此引用永远不会悬空.


Jon*_*ely 12

如果返回一个向量项,那么lambda表达式的返回类型是什么?

这是错误的问题.

如果没有明确指定 lambda表达式,你应该问什么是返回类型.

答案在C++ 11 5.1.2 [expr.prim.lambda]第5段中给出,其中它表示如果lambda没有return expr;返回的语句void,否则:

lvalue-to-rvalue转换(4.1),数组到指针转换(4.2)和函数到指针转换(4.3)之后返回表达式的类型;

(请参阅下面的TC对DR 1048的评论,它略微改变了这个规则,编译器实际上实现了更改的规则,但在这种情况下并不重要.)

lvalue-to-rvalue转换意味着如果return语句返回一个左值,就像v[i]它衰减到一个右值,即它只返回int值.

所以你的代码的问题是lambda返回一个临时的,但是std::function<const int&(int)>包装它会绑定对那个临时的引用.当您尝试打印出临时值已经消失的值(它已经绑定到一个不再存在的堆栈帧的对象),因此您有未定义的行为.

修复是使用std::function<int(int)>或确保lambda返回有效的引用,而不是rvalue.

在C++ 14中,非lambdas也可以使用返回类型推导,例如:

auto f(std::vector<int>& v, int i) { return v[i]; }
Run Code Online (Sandbox Code Playgroud)

这遵循与lambda的C++ 11规则类似(但不完全相同)的规则,因此返回类型为int.要返回引用,您需要使用:

decltype(auto) f(std::vector<int>& v, int i) { return v[i]; }
Run Code Online (Sandbox Code Playgroud)


seh*_*ehe 5

  1. 我假设第一个lambda可能会返回intint&¹.不是const int&因为引用v不是const对象(捕获本身不可变是无关紧要的,因为那是引用本身)

  2. 当绑定到const-ref时,临时值的生命周期延伸到封闭范围的末尾


¹我稍后会尝试深入研究.就目前而言,我会定期演绎直觉,并说auto会产生假设int,而auto&会推断int&,所以我希望实际的回归类型是int.如果有人打败我,那就更好了.我几个小时都没有时间

请参阅@milleniumbug提供的测试: Live On Coliru