No warning when lambda returns reference to temporary

Hiya,

I ran into this (presumably) undefined behaviour today, and wonder if it’s a bug or expected. My app was coded similarly and ran correctly on macOS/LLVM version 8.1.0 (clang-802.0.38) but not Ubuntu14.04/gcc-4.7.3-12ubuntu1

In the case of this code, it’s invalid since the lambda returns a reference to a temp object. My bad, I’ll fix it. Seems the compiler could’ve warned me though :slight_smile:

Compiler warns when explicitly specifying the return type of the lambda using -> operator, but not when implicit. Is this expected or a bug?

Thanks in advance,
Matt

// Minimal test case:

#include

#include

void print_stuff(std::function<const int&()> f) {

std::cout << "stuff is: " << f() << std::endl;

}

int main(int argc, char *argv) {

print_stuff( {

int i = 123;

return i;

});

}

Hi Matthew,

When you don't specify a return type for the lambda, the compiler
deduces 'int', not 'int&'.

auto deduced = () { int i = 123; return i; };
auto spelled_out = () -> int& { int i = 123; return i; };

int d = deduced;
int s = spelled_out;

This doesn't compile of course, but the error messages are the useful info:

$ g++-8 -std=c++11 is_it_ref.cpp
is_it_ref.cpp: In lambda function:
is_it_ref.cpp:2:39: warning: reference to local variable ‘i’ returned
[-Wreturn-local-addr]
auto spelled_out = () -> int& { int i = 123; return i; };
                                       ^
is_it_ref.cpp: At global scope:
is_it_ref.cpp:4:9: error: invalid user-defined conversion from
‘<lambda()>’ to ‘int’ [-fpermissive]
int d = deduced;
         ^~~~~~~
is_it_ref.cpp:1:23: note: candidate is: ‘<lambda()>::operator int
(*)()() const’ <near match>
auto deduced = () { int i = 123; return i; };
                       ^
is_it_ref.cpp:1:23: note: no known conversion from ‘int (*)()’ to ‘int’
is_it_ref.cpp:5:9: error: invalid user-defined conversion from
‘<lambda()>’ to ‘int’ [-fpermissive]
int s = spelled_out;
         ^~~~~~~~~~~
is_it_ref.cpp:2:31: note: candidate is: ‘<lambda()>::operator int&
(*)()() const’ <near match>
auto spelled_out = () -> int& { int i = 123; return i; };
                               ^
is_it_ref.cpp:2:31: note: no known conversion from ‘int& (*)()’ to ‘int’

Hi Csaba,

Thanks very much for the reply - makes sense. I was originally having trouble with strings, but switched to ints for a shorter “minimal case.”

In this case since the lambda returns an int, it’s now std::function<int()> not std::function<const int&()> which on the surface seems it should be a mismatching type.

Minimal case updated for strings below. Strangely, it works correctly on macOS but invokes undefined behaviour on ubuntu.

Cheers,
Matt

#include

#include

#include

void print_stuff(std::function<const std::string&()> f) {

std::cout << "stuff is: " << f() << std::endl;

}

int main(int argc, char *argv) {

print_stuff(& {

std::stringstream ss;

ss << argc;

return ss.str();

});

}

Hi Csaba,

Thanks very much for the reply - makes sense. I was originally having trouble with strings, but switched to ints for a shorter “minimal case.”

In this case since the lambda returns an int, it’s now std::function<int()> not std::function<const int&()> which on the surface seems it should be a mismatching type.

So this is where the kicker/punchline is - a lambda isn’t a std::function, a lambda is just its own type with an op() overload.

std::function wraps any /compatible/ callable.

So basically if you have some callable object ‘f’ and this is valid:

T1 g(T2 t2, ) {
return f(std::forward(t2), );
}

then ‘f’ is implicitly convertible to std::function<T1(T2, )>

What that means in reality is that, yes, because this is valid (yet buggy):

int f();
const int &g() {
return f();
}

so is this:

std::function<const int&()> g = f;

You can get similar kinds of issues with parameters too:

void f(bool);
std::function<void(T*)> g = f; //valid but perhaps surprising (f(true) if g(non-null), f(false) if g(null-pointer))