I found the following in cppreference.com in the documentation for std::function:
Care should be taken when a std::function whose result type is a reference is initialized from a
lambda expression without a trailing-return-type. Due to the way auto deduction works, such
lambda expression will always return a prvalue. Hence, the resulting reference will usually bind
to a temporary whose lifetime ends when std::function::operator() returns.
1 2
std::function<constint&()> F([]{ return 42; });
int x = F(); // Undefined behavior: the result of F() is a dangling reference
mingw - g++ (x86_64-posix-seh-rev0, Built by MinGW-W64 project) 8.1.0
g++ -std=c++17 main.cpp -o main
Program is:
1 2 3 4 5 6 7
#include <functional>
int main()
{
std::function<constint&()> F([]{ return 42; });
int x = F();
}
Segmentation fault (core dumped)
Crashes in both cases.
PS: I am in no way claiming I understand exactly what's going on here, only that it appears the cppreference article is correct (and even if it didn't crash for me, I would still not bet against cppreference).
Have you not bothered reading the thread?
The point is that there's "no dangling reference" FOR YOU, AT THIS TIME, WITH THE EXACT SYSTEM AND COMPILER THAT YOU HAPPENED TO USE. If that's all you care about, then fine.
For example, if I compile it with clang++ it runs, but if I compile it with g++ it segfaults.
Do you understand the problem now???
so if my reasoning is correct, y refers to a const int returned by F() which is created in that call and is destroyed at the end of that line?? Is this the dangling reference?
I understand @dutch.... you are saying that there is a dangling reference that my system and compiler is not "getting", not producing any problem but the dangling is there anyway and I cannot trust this code as it is, because any compiler enhancement could very well show the ugly face of the dangling reference?
Yeah, that's the idea. The important point is that if a trusted source says something is U.B. then you cannot really disprove it with any particular system / configuration.
Even for g++ it will not segfault if you turn on optimization.
In the online compiler Ganado links to, you can do that in the tool menu (upper right) and add "extra compiler flags" (add -O).
I still don't understand. Doesn't the fact that it's a const int& expand the lifetime of the temporary?
e.g. doing constint& foo = 42;
Clearly I am mistaken, but I don't understand why.
> Doesn't the fact that it's a const int& expand the lifetime of the temporary?
See:
Lifetime of a temporary
Whenever a reference is bound to a temporary or to a subobject thereof, the lifetime of the temporary is extended to match the lifetime of the reference, with the following exceptions:
a temporary bound to a return value of a function in a return statement is not extended: it is destroyed immediately at the end of the return expression. Such function always returns a dangling reference.
...
In general, the lifetime of a temporary cannot be further extended by "passing it on": a second reference, initialized from the reference to which the temporary was bound, does not affect its lifetime. https://en.cppreference.com/w/cpp/language/reference_initialization#Lifetime_of_a_temporary
Doesn't the fact that it's a const int& expand the lifetime of the temporary?
No, because this... is an exception to the rule.
The lifetime of a temporary bound to the returned value in a function return statement ([stmt.return]) is not extended; the temporary is destroyed at the end of the full-expression in the return statement.
My (technically wrong) mental model is that return 42 initializes a int const&, which extends the lifetime of the temporary until the (imminent) end of the function. Maybe that's helpful/easier to remember.