Consider the following snippet:
#include
#include
#include
int main()
{
std::vectorv = {
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]; }
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)
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