In the C++14 standard § 5.1.2/12 it shows an example of a lambda expression that apparently seems to be able to refer to a reaching scope's variable x
, even though:
- the capture list is empty, i.e. no capture-default
- the comment says that it "does not capture
x
"
Here's the example:
void f(int, const int (&)[2] = {}) { } // #1
void test() {
const int x = 17;
auto g = [](auto a) {
f(x); // OK: calls #1, does not capture x
};
}
See that it does compile . It seems to hinge on x
being const
; if the const
is removed, it no longer compiles for the reasons one would expect (capture list is empty). It happens even if I make the parameter be int
so that it's no longer a generic lambda.
How is it possible for the lambda to refer to x
even though the capture list is empty? And how is this possible while at the same time apparently not capturing x
(as the comment says)?
The closest thing I found on this subject was someone else tangentially noticing this in a comment.
Here's the full section 5.1.2/12 from the standard:
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.
[ Example:
void f(int, const int (&)[2] = {}) { } // #1
void f(const int&, const int (&)[1]) { } // #2
void test() {
const int x = 17;
auto g = [](auto a) {
f(x); // OK: calls #1, does not capture x
};
auto g2 = [=](auto a) {
int selector[sizeof(a) == 1 ? 1 : 2]{};
f(x, selector); // OK: is a dependent expression, so captures x
};
}
—end example ] All such implicitly captured entities shall be declared within the reaching scope of the lambda expression. [ Note: The implicit capture of an entity by a nested lambda-expression can cause its implicit capture by the containing lambda-expression (see below). Implicit odr-uses of this can result in implicit capture. —end note ]
1条答案
按热度按时间jk9hmnmh1#
You have the right quote. A variable needs to be captured if it is odr-used. ODR-use means basically that the variable is used in a context where it needs a definition. So either its address is taken, or a reference is taken to it, etc. One key exception is, from [basic.def.odr]:
A variable
x
whose name appears as a potentially-evaluated expressionex
is odr-used byex
unless applying the lvalue-to-rvalue conversion (4.1) tox
yields a constant expression (5.20) that does not invoke any nontrivial functions and, ifx
is an object,ex
is an element of the set of potential results of an expressione
, where either the lvalue-to-rvalue conversion (4.1) is applied to e, or e is a discarded-value expression (Clause 5).So in your example, applying lvalue-to-rvalue conversion on
x
yields a constant expression (sincex
is a constant integral), so it's not odr-used. Since it's not odr-used, it doesn't have to be captured.On the other hand, if
x
were bound to a reference (e.g.f
took its argument asconst int&
), then it would be odr-used, and so would have to be captured. In the second example presented,x
's "odr-use-ness" is dependent on what the generic lambda argument is, so that is considered captured anyway for sanity's sake.