[expr.prim.lambda.capture]/7 :
If an expression potentially references a local entity within a scope in which it is odr-usable, and the expression would be potentially evaluated if the effect of any enclosing typeid expressions ([expr.typeid]) were ignored, the entity is said to be implicitly captured by each intervening lambda-expression with an associated capture-default that does not explicitly capture it. The implicit capture of *this is deprecated when the capture-default is =; see [depr.capture.this]. [Example 4:
void f(int, const int (&)[2] = {});
void test() {
const int x = 17;
auto g = [](auto a) {
f(x); // OK, calls #1, does not capture x
};
auto g1 = [=](auto a) {
f(x); // OK, calls #1, captures x
};
}
... Within g1, an implementation can optimize away the capture of x as it is not odr-used. — end example]
So an entity is captured even if it is not odr-used by the lambda body, and the example below says that the implementations can optimize it away. Therefore, since the implementations can optimize it away, why add such a rule? What's the point of it?
cppreference says it was added in C++17. What's the original proposal?
P0588R0 contains the rationale explaining why the rules for implicit capture were changed in order to capture some variables that are not going to be odr-used anyway. It's very subtle.
Basically, in order to determine the size of a lambda closure type, you need to know which variables are captured and which ones are not, but under the old rules:
The paper gives the following example:
template<typename T> void f(T t) {
auto lambda = [&](auto a) {
if constexpr (Copyable<T>() && sizeof(a) == 32) {
T u = t;
} else {
// ... do not use t ...
}
};
// ...
}
When f
is instantiated with a non-copyable type, ideally we would like the branch with T u = t;
to be discarded. That's what the if constexpr
is there for. But an if constexpr
statement does not discard the un-taken branch until the point at which the condition is no longer dependent---meaning that the type of a
must be known before T u = t;
can be discarded. Unfortunately, under the old rules, T
must be substituted into T u = t;
in order to determine whether t
is captured, and this happens before any opportunity to discard this statement
The new rules simply declare that t
is captured; therefore, the compiler doesn't have to perform any substitution into the lambda body until the point at which a specialization of the function call operator is referenced (and thus, its body can be fully instantiated). And if the compiler is somehow able to prove that t
will never be odr-used by any possible specialization of the function call operator, it's free to optimize it out.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.