What I'm trying to do is eat exceptions when constructing an object that may be invalid. It'd be perfect for use of std::optional
, but I don't believe the omission of std::optional
changes the error I see: the object is being captured and used before it's been initialized. I don't believe it should be captured in the first place because we haven't reached a sequence point to the best of my knowledge (does a lambda initialization count as a sequence point?). Moreover, the bug is IMO easily catchable human error (and even does get caught... depending upon circumstances).
How (more importantly, why) is the lambda able to capture and use the not-yet-initialized foo
?
#include <string>
using namespace std;
void foo() {
string foo = [&]()->string{
// using foo before it's been initialized == undefined behavior
auto guessed_foo = to_string(1234);
if ( begin(foo) == end(foo) ) {
return guessed_foo;
}
return {};
}();
}
Compiler exited with result code 0
But... replacing the declaration of string foo
with auto foo
does appear to cause an error similar to what I'd like to see.
#include <string>
using namespace std;
void foo() {
auto foo = [&]()->string{
auto guessed_foo = to_string(1234);
if ( begin(foo) == end(foo) ) {
return guessed_foo;
}
return {};
}();
}
error: variable 'foo' declared with 'auto' type cannot appear in its own initializer
Note that I found this using GCC 6.2 on Ubuntu 16.04 LTS. The configuration in Godbolt is using clang 3.9.1. Both are configured for c++14.
So my questions are:
- Why is the lambda capture for an initialization of a non-auto-declared variable able to capture and use the (not-yet-initialized) variable?
- Why does
auto
get (in my opinion, correctly) caught and errored? - Moreover, why the difference between the above two? It sounds like compiler bugs, but... is there something specific in the standard declaring this to be correct behavior?
- This could be taken as an argument for
auto
keyword?
The second snippet runs into [dcl.spec.auto]/10:
The type of
foo
is needed to determine the type of the expressionfoo
within the lambda body, but at that point you haven't deducedfoo
's type yet, so the program is ill-formed.As to why you are allowed to capture something before its initialization, see generally Why is 'int i = i;' legal?. We have many examples of recursive lambdas using
std::function
: