# Options for lambdas

 org: ISO/IEC JCT1/SC22/WG14 document: N2893 target: IS 9899:2023 version: 1 date: 2021-12-25 license: CC BY

## Revision history

Paper number Title Changes
N2893 Options for lambdas combines optional features on top of N2892:
default identifier captures from N2737
syntax disambiguation for [ [ … from N2736

## Introduction

N2892 now introduces two main forms of captures for lambdas

• Value captures are explicit names for values that are evaluated (“frozen”) at the point of the evaluation of the lambda. They have the form identifier = expression.
• Identifier captures are explicitly propagating the use of a local variable name into the lambda body. They have the form &identifier.

Note that for a value capture identifier becomes a per-call local variable with that name, much as a function parameter. The rules for evaluation are made that they are similar to function parameters, that is within expression a use of identifier does not refer to the capture, but to a variable of a surrounding scope, if that exists, or is a syntax error, if it doesn’t.

Not covered by N2892 are what we call shadow captures in the following. These just list an identifier identifier and are a shortcut for the special value capture identifier = identifier, that is an lvalue conversion of identifier and a creation of a new object with the same name that holds that value and that shadows the usage of the outer variable for all calls to the lambda.

Also not covered by N2892 are default captures in the form of & or =.

## Proposed optional features

### Default captures

There has been some criticism against default captures = and & because they would weaken the model of isolation and, for =, shadow the use of variables from an outer scope in an unusual way. Nevertheless, we think that it would be important to have both features, such that users of existing extension could migrate more easily. Namely users of Objective C’s blocks would be better served with =, where users of gcc’s nested functions or compound expressions would be better served with &.

#### Design

Already the C++ feature only allows for three types of capture lists.

• A mixed lists that has explicit identifier and value captures, as now in our basic lambda proposal N2892
• A default identifier list that starts with a & token and then only has value or shadow captures that denote exceptions from the default.
• A default shadow list that starts with a = token and then only has identifier captures that denote exceptions from the default.

We now propose to clearly separate these three possibilities syntactically, with three different syntax terms mixed-capture-list, default-identifier-capture-list and default-shadow-capture-list. The latter two are comprised into a default-capture-list such that the term is directly introduced through the syntax.

This first option already introduces shadow captures for two of the cases.

• The definition is needed to explain default shadow captures. (But here they don’t occur syntactically.)
• They are added to the exception list of default identifier captures, but without introducing new syntax ambiguities.

On the other hand shadow captures are not strictly needed for the full semantics. They have been seen critical by some for their property (now reflected in the term itself) that they shadow the visibility of a outer variable inside the function body of a lambda. Therefore the first option does not add them to mixed capture lists.

A second option introduces the term with a subset of the tools described above and anchors them additionally into “capture list element”.

Such an addition looks relatively innocent but it has two drawbacks:

• It introduces a syntax ambiguity with attributes.
• Shadowing of local variables, either by direct reuse of an identifier in a value capture or by a shadow capture can obfuscate the workings of a lambda expression substantially.

### Syntax ambiguity

The new grammar as proposed has two new lexical ambiguities.

• A start sequence [identifier] or [identifier = expression] may be an array element designator idexed by a variable or an assignment expression (6.7.12 p1) or start a capture clause (6.5.2.6 p1).
• A start sequence [ [ may be the start of an attribute specifier (6.7.15.1 p1) or the start of several other constructs that allow an expression within an array bound (6.7.8.2 p1 and 6.7.9 p1 or array subscript 6.5.2 p1.

The first may be resolved immediately after a token sequence as indicated above has been scanned. It only requires limited lookahead for resolution, although the occurrence of commas may complicate parsing. Therefore we don’t think this ambiguity needs otherwise to be resolved normatively. WG14 could add a rule that gives priority to the designator reading and force lambda expressions that are used in initializers to be surrounded by parenthesis, but this should be proposed in a different paper.

With the introduction of shadow captures, the second ambiguity needs unbounded lookahead and is therefore more challenging for implementors. C++ helps implementors, here, in making the appearance of a cosecutive pair [ [ other than in attributes undefined. This imposes some care for applications, because with that rule they have to surround lambda expressions with parenthesis, for example in declarations of VLA. We propose this approach as a possible option for WG14.

## Proposed wording

### Default captures

Change in 6.5.2.6 p1, syntax

capture-list:

mixed-capture-list

default-capture-list

mixed-capture-list:

capture-list-element

mixed-capture-list , capture-list-element

default-capture-list:

default-identifier-capture-list

=

default-shadow-capture-list , identifier-capture

default-identifier-capture-list:

&

default-identifier-capture-list , value-capture

default-identifier-capture-list , shadow-capture

capture

Change in 6.5.2.6 p2, constraints

2 An identifier shall appear at most once; either as a capture or as a parameter name in the parameter list. The identifier of an identifier or shadow capture shall designatebe the name of an object of automatic storage duration that is defined in a scope that surrounds the lambda expression. For a shadow capture that object shall not have array type.

Add a new paragraph 6.5.2.6 p3’ before p4 to the constraints

3’ If there is an identifier id that is not explicitly listed as a capture, id is defined with automatic storage duration in a surrounding scope and id would be evaluated in the function body, the capture list shall be a default capture list; id is an implicit capture. The effect is the same as for a shadow capture id or identifier capture &id, respectively; all constraints for these implicit captures apply.

Add a new paragraph 6.5.2.6 p8’ before p9

8’ The identifier of the object of automatic storage duration of the surrounding scope with the same name as a shadow capture shall be visible at the point of evaluation of the lambda expression. If the identifier of the shadow capture is id, the effect is as if a value capture of the form id = id is used and the declared shadow capture shadows the access to the object of the surrounding scope until the end of the function body.

Add a new paragraph 6.5.2.6 p9’ before p10

9’ The lvalue conversions of the objects of surrounding scopes corresponding to implicit shadow captures are indeterminately sequenced at the beginning of the evaluation of the lambda expression and the implicit shadow captures shadow all further usage of these objects until the end of the function body.

Add the syntax element shadow-capture as above and change in 6.5.2.6 p1, syntax

capture-list-element:

value-capture

1. White-space characters separating tokens are no longer significant. Each preprocessing token is converted into a token. The resulting tokens are syntactically and semantically analyzed and translated as a translation unit.; two consecutive [ tokens shall be the initial token sequence of an attribute specifier or of a balanced token (6.7.11.1).