45

This compiles:

int main() {
    const int x = 123;
    auto g = []() { std::cout << x << "\n"; };
    g();
}

But this:

int main(){
    const float x = 123;
    auto g = []() { std::cout << x << "\n"; };
    g();
}

produces:

"error: 'x' is not captured"

Why?

I've tested it on both GCC (various versions from 5.0.0 to 8.0.0) and Clang (various versions from 4.0.0 to 6.0.0). It behaves the same in all cases.

gsamaras
  • 71,951
  • 46
  • 188
  • 305
rubix_addict
  • 1,811
  • 13
  • 27
  • 1
    http://en.cppreference.com/w/cpp/language/lambda You need to specify the variables to capture – StijnvanGaal Oct 13 '17 at 07:07
  • 7
    @StijnvanGaal: I have the impression the OP knows that. – Bathsheba Oct 13 '17 at 07:07
  • 1
    What compiler and version is this? – deviantfan Oct 13 '17 at 07:09
  • 18
    @csm_dev OP did test out other types, and came to the conclusion some are implicitly captured while others aren't. OP is also obviously aware that lambdas shouldn't do that, these are all research effort – Passer By Oct 13 '17 at 07:16
  • 5
    @Bathsheba I've tested it on both GCC (various versions from 5.0.0 to 8.0.0) and Clang (various versions from 4.0.0 to 6.0.0). It behaves the same in all cases. – rubix_addict Oct 13 '17 at 07:24
  • 8
    @rubix_addict: please **edit your question** to explain how you tested it (don't leave that just in comments) – Basile Starynkevitch Oct 13 '17 at 07:28
  • 1
    Note: when providing an empty closure list, a variable still can be captured if it is not odr-used within the lambda. Outputting the address of the integer will cause the compilation to fail again. – Jodocus Oct 13 '17 at 07:33

2 Answers2

38

Lambda's scope can implicitly capture variables within its reaching scope.

Your variables are in the reaching scope, since they are local to the (main) function that defines the lambda.

However, there are certain criteria in which variables can be captured via this mechanism, as mentioned in [expr.prim.lambda]/12:

A lambda-expression with an associated capture-default that does not explicitly capture this or a variable with automatic storage duration [..], is said to implicitly capture the entity (i.e., this or a variable) if the compound-statement:

-odr-uses ([basic.def.odr]) the entity, or

-names the entity in a potentially-evaluated expression ([basic.def.odr]) where the enclosing full-expression depends on a generic lambda parameter declared within the reaching scope of the lambda-expression.

The most important part is in [expr.const]/2.7:

A conditional-expression e is a core constant expression unless the evaluation of e, [..] would evaluate one of the following expressions:

an lvalue-to-rvalue conversion ([conv.lval]) unless it is applied to:

a non-volatile glvalue of integral or enumeration type that refers to a non-volatile const object with a preceding initialization, initialized with a constant expression.

So const int is a core constant expression while const float is not.

Moreover [expr.const]1826 mentions:

A const integer initialized with a constant can be used in constant expressions, but a const floating point variable initialized with a constant cannot.

Read more in Why is a const variable sometimes not required to be captured in a lambda?

gsamaras
  • 71,951
  • 46
  • 188
  • 305
  • 2
    Do you have a ref for (2) (other than linking the whole standard - respect!) ? This seems to be on the right lines. Have an upvote. – Bathsheba Oct 13 '17 at 07:51
  • 1
    @Bathsheba §5.1.2 - (12) – bolov Oct 13 '17 at 07:55
  • @Bathsheba but I still can't make head or tails of the paragraph... – bolov Oct 13 '17 at 07:56
  • 3
    -1 - I don't see anything in 5.1.2 that remotely resembles the quote in the comment. The only relevant qualification according to p13 seems to be "a variable with automatic storage duration from its reaching scope". – Michael Foukarakis Oct 13 '17 at 07:57
  • 1
    @MichaelFoukarakis look at the example – bolov Oct 13 '17 at 07:58
  • 4
    @bolov I can't get how does the example explain that `const float` should be captured explicitly? – songyuanyao Oct 13 '17 at 08:01
  • 1
    @songyuanyao I don't understand the paragraph... I just remarked that the example indicates that this is indeed the paragraph that deals with this. – bolov Oct 13 '17 at 08:02
  • 1
    @bolov Well I just think it seems irrelevant because the point of the question should be the difference between `const int` and `const float`, which I can't get from this answer; the 2nd sentence in the quote says about that but I can't find it in the standard and how comes to that conclusion. – songyuanyao Oct 13 '17 at 08:13
  • 1
    @songyuanyao, that's a reference to expr.const#2.7. This comment needs a lot of work to reach the quality of the linked one, tbh. – Michael Foukarakis Oct 13 '17 at 08:16
  • @MichaelFoukarakis I think you're right, the point seems to be that `const int` is a *core constant expression* while `const float` is not, which is explained in expr.const#2.7. Anyway thanks for pointing that, now I get my answer. – songyuanyao Oct 13 '17 at 08:22
9

C++14 draft N4140 5.1.2.12 [expr.prim.lambda] :

A lambda-expression with an associated capture-default that does not explicitly capture this or a variable with automatic storage duration (this excludes any id-expression that has been found to refer to an init-capture’s associated non-static data member), is said to implicitly capture the entity (i.e., this or a variable) if the compound-statement:

odr-uses (3.2) the entity, or

names the entity in a potentially-evaluated expression (3.2) where the enclosing full-expression depends on a generic lambda parameter declared within the reaching scope of the lambda-expression.

Also, .open-std.org :

A const integer initialized with a constant can be used in constant expressions, but a const floating point variable initialized with a constant cannot. This was intentional, to be compatible with C++03 while encouraging the consistent use of constexpr. Some people have found this distinction to be surprising, however.

It was also observed that allowing const floating point variables as constant expressions would be an ABI-breaking change, since it would affect lambda capture.

msc
  • 33,420
  • 29
  • 119
  • 214