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


2569. Use of decltype(capture) in a lambda's parameter-declaration-clause

Section: 7.5.5.2  [expr.prim.id.unqual]     Status: CD6     Submitter: Barry Revzin     Date: 2022-04-16     Liaison: EWG

[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:

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

[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: ...

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