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)
因此,我想知道:
在第二个片段中,lambda表达式的返回类型是f什么?是f一样的g吗?
在第一个片段中,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)
我假设第一个lambda可能会返回int或int&¹.不是const int&因为引用v不是const对象(捕获本身不可变是无关紧要的,因为那是引用本身)
当绑定到const-ref时,临时值的生命周期延伸到封闭范围的末尾
¹我稍后会尝试深入研究.就目前而言,我会定期演绎直觉,并说auto会产生假设int,而auto&会推断int&,所以我希望实际的回归类型是int.如果有人打败我,那就更好了.我几个小时都没有时间
请参阅@milleniumbug提供的测试: Live On Coliru
| 归档时间: |
|
| 查看次数: |
3320 次 |
| 最近记录: |