lambda capture during initialization should be an

2019-06-25 09:48发布

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?

https://godbolt.org/g/IwcHrV

#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.

https://godbolt.org/g/GfE4WH

#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?

1条回答
老娘就宠你
2楼-- · 2019-06-25 10:41

The second snippet runs into [dcl.spec.auto]/10:

If the type of an entity with an undeduced placeholder type is needed to determine the type of an expression, the program is ill-formed.

The type of foo is needed to determine the type of the expression foo within the lambda body, but at that point you haven't deduced foo'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:

std::function<void(int)> foo = [&foo](int i){ return foo(i - 1); };
查看更多
登录 后发表回答