This is an unofficial snapshot of the ISO/IEC JTC1 SC22 WG21 Core Issues List revision 115d. See http://www.open-std.org/jtc1/sc22/wg21/ for the official list.
2024-10-26
decltype(capture)
in a lambda's parameter-declaration-clause
[Accepted at the July, 2022 meeting as part of paper P2579R0 (Mitigation strategies for P2036 "Changing scope for lambda trailing-return-type").]
Paper P2036R3 disallowed using captures in the parameter-declaration-clause of a lambda, because it is not yet known at that point whether the lambda is going to be mutable, and thus the type of an expression referring to a capture may or may not be const. Such problematic uses of captures are now ill-formed. The paper was approved as a Defect Report, recommending to implementers to apply the change to all language modes back to C++11.
However, that broke legitimate uses in popular implementations of the standard library such as:
[local_end](decltype(local_end) it) { return it != local_end; };
As specified in 9.2.9.6 [dcl.type.decltype] bullet 1.3, decltype(local_end) does not depend on whether the lambda ends up being mutable or not:
- otherwise, if E is an unparenthesized id-expression or an unparenthesized class member access (7.6.1.5 [expr.ref]), decltype(E) is the type of the entity named by E. If there is no such entity, the program is ill-formed;
Possible approaches (not necessarily exclusive):
Suggested resolution (carves out an exception for decltype, sizeof, noexcept):
Change in 7.5.5.2 [expr.prim.id.unqual] paragraph 3 as follows:
An unqualified-id is mutable-agnostic if it is the operand of a decltype (9.2.9.6 [dcl.type.decltype]), sizeof (7.6.2.5 [expr.sizeof]), or noexcept (7.6.2.7 [expr.unary.noexcept]). If the unqualified-id appears in a lambda-expression at program point P and the entity is a local entity (6.1 [basic.pre]) or a variable declared by an init-capture (7.5.6.3 [expr.prim.lambda.capture]), then let S be the compound-statement of the innermost enclosing lambda-expression of P. If naming the entity from outside of an unevaluated operand within S would refer to an entity captured by copy in some intervening lambda-expression, then let E be the innermost such lambda-expression, and:...
- If the unqualified-id is mutable-agnostic or P is in E's function parameter scope but not its parameter-declaration-clause, then the type of the expression is the type of a class member access expression (7.6.1.5 [expr.ref]) naming the non-static data member that would be declared for such a capture in the object parameter (9.3.4.6 [dcl.fct]) of the function call operator of E. [Note 3: If E is not declared mutable, the type of such an identifier will typically be const qualified. —end note]
- Otherwise (if the unqualified-id is not mutable-agnostic and P either precedes E's function parameter scope or is in E's parameter-declaration-clause), the program is ill-formed.
[Example:
[=]<decltype(x) P>{}; // ok: P has type float [=]<decltype((x)) P>{}; // error: x refers to local entity but precedes the // lambda's function parameter scope [=](decltype((x)) y){}; // error: x refers to local entity but is in the lambda's // parameter-declaration-clause-- end example]
Suggested resolution (carves out an exception for decltype only):
Change in 7.5.5.2 [expr.prim.id.unqual] paragraph 3 as follows:
If the unqualified-id appears in a lambda-expression at program point P and the entity is a local entity (6.1 [basic.pre]) or a variable declared by an init-capture (7.5.6.3 [expr.prim.lambda.capture]), then let S be the compound-statement of the innermost enclosing lambda-expression of P. If naming the entity from outside of an unevaluated operand within S would refer to an entity captured by copy in some intervening lambda-expression, then let E be the innermost such lambda-expression, and:...
- If the unqualified-id is the operand of a decltype (9.2.9.6 [dcl.type.decltype]) or P is in E's function parameter scope but not its parameter-declaration-clause, then the type of the expression is the type of a class member access expression (7.6.1.5 [expr.ref]) naming the non-static data member that would be declared for such a capture in the object parameter (9.3.4.6 [dcl.fct]) of the function call operator of E. [Note 3: If E is not declared mutable, the type of such an identifier will typically be const qualified. —end note]
- Otherwise (if the unqualified-id is not the operand of a decltype and P either precedes E's function parameter scope or is in E's parameter-declaration-clause), the program is ill-formed.
[Example:
[=]<decltype(x) P>{}; // ok: P has type float [=]<decltype((x)) P>{}; // error: x refers to local entity but precedes the // lambda's function parameter scope [=](decltype((x)) y){}; // error: x refers to local entity but is in the lambda's // parameter-declaration-clause-- end example]
Additional notes (April, 2022):
Forwarded to EWG with paper issue 1227, by decision of the CWG chair.
See paper P2579R0 for a more detailed discussion of the issue.