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

吃可爱长大的小学妹 提交于 2019-11-28 10:42:31

The return type of a lambda uses the auto return type deduction rules, which strips the referenceness. (Originally it used a slightly different set of rules based on lvalue-to-rvalue conversion (which also removed the reference), but that was changed by a DR.)

Hence, [&v](int i) { return v[i];}; returns int. As a result, in std::function<const int&(int)> f = [&v](int i) { return v[i];};, calling f() returns a dangling reference. Binding a reference to a temporary extends the lifetime of the temporary, but in this case the binding happened deep inside std::function's machinery, so by the time f() returns, the temporary is gone already.

g(3) is fine because the const int & returned is bound directly to the vector element v[i], so the reference is never dangling.

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

That's the wrong question.

You should be asking what is the return type of a lambda expression if it is not specified explicitly?.

The answer is given in C++11 5.1.2 [expr.prim.lambda] paragraph 5, where it says if the lambda has no return expr; statement it returns void, otherwise:

the type of the returned expression after lvalue-to-rvalue conversion (4.1), array-to-pointer conversion (4.2), and function-to-pointer conversion (4.3);

(See T.C.'s comment below for DR 1048 which changed this rule slightly, and compilers actually implement the changed rule, but it doesn't matter in this case.)

lvalue-to-rvalue conversion means that if the return statement returns an lvalue like v[i] it decays to an rvalue, i.e. it returns just int by value.

So the problem with your code is that the lambda returns a temporary, but the std::function<const int&(int)> that wraps it binds a reference to that temporary. When you try to print out the value the temporary has aalready gone away (it was bound to an object a stack frame that no longer exists) so you have undefined behaviour.

The fix is to use std::function<int(int)> or ensure the lambda returns a valid reference, not an rvalue.

In C++14 non-lambdas can also use return type deduction, e.g.:

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

This follows similar (but not identical) rules to the C++11 rules for lambdas, so the return type is int. To return a reference you need to use:

decltype(auto) f(std::vector<int>& v, int i) { return v[i]; }
  1. I assume the first lambda might return int or int&¹. Not const int& as the reference v is not to a const object (it doesn't matter that the capture itself is not mutable, since that's the reference itself)

  2. Life times of temporaries get extended to the end of the enclosing scope when bound to a const-ref


¹ I'll try to dive into that later. For now, I'm going with regular deduction intuition and say that auto would decuce int, whereas auto& would deduce int&, so I expect that the actual return type is int. If anyone beats me to it, all the better. I won't have time for a few hours

See the test supplied by @milleniumbug: Live On Coliru

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!