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

What is the return type of a lambda expression if an item of a vector is returned?

本文关键字:lambda 表达式 是什么 返回类型 一个 返回 向量 如果      更新时间:2023-10-16

考虑以下片段:

#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;
}

我期待着同样的结果:在f中,v是通过const引用传递的,所以v[i]应该具有const int&类型。

然而,我得到的结果

0 3

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

#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;
}

输出:

3 3

因此我想知道:

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

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

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 &直接绑定到向量元素v[i],所以引用从不悬空。

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

这是个错误的问题。

您应该问,如果lambda表达式没有显式指定,那么它的返回类型是什么

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

左值到右值转换(4.1)、数组到指针转换(4.2)和函数到指针转换后返回的表达式的类型(4.3);

(请参阅下面T.C.对DR 1048的评论,DR 1048略微更改了此规则,编译器实际上实现了更改后的规则,但在这种情况下无关紧要。)

左值到右值的转换意味着,如果return语句返回类似v[i]的左值,则它衰减为右值,即按值仅返回int

因此,代码的问题是lambda返回一个临时的,但封装它的std::function<const int&(int)>将引用绑定到该临时的。当你试图打印出临时值时,它已经消失了(它被绑定到一个不再存在的堆栈框架中的对象),所以你有未定义的行为。

修复方法是使用std::function<int(int)>或确保lambda返回有效的引用,而不是右值。

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

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

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

decltype(auto) f(std::vector<int>& v, int i) { return v[i]; }
  1. 我假设第一个lambda可能返回intint&。不是const int&,因为引用v不是常量对象(捕获本身不可变并不重要,因为这是引用本身)

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


我稍后会深入研究。现在,我用常规的推导直觉,说auto会推导出int,而auto&会推断出int&,所以我预计实际的返回类型是int。如果有人打败我,那就更好了。我没有几个小时的时间

查看@menniumbug提供的测试:Live On Coliru