Skip to content

CWG2883 [basic.def.odr] Using captures in lambda bodies always disallowed because odr-usable definition does not account for lambda scopes #523

@hubert-reinterpretcast

Description

@hubert-reinterpretcast

Full name of submitter (unless configured in github; will be published with the issue): Hubert Tong

Reference (section label): basic.def.odr

Link to reflector thread (if any): N/A

Issue description:
https://eel.is/c++draft/basic.def.odr#10 defines odr-usable and has a rule:

If a local entity is odr-used in a scope in which it is not odr-usable, the program is ill-formed.

One of the requirements for a local entity to be odr-usable, is that there are no intervening scopes other than block scopes and function parameter scopes.

Consider:

void f() {
  int x;
  [&] { return x; };
}

There is a lambda scope (https://eel.is/c++draft/basic.scope.lambda) between the block scope of the compound-statement and the definition of x. It follows that the above code is ill-formed (according to the wording).

Consider also:

struct A {
  A() = default;
  A(const A &) = delete;
  constexpr operator int() { return 42; }
};
void f() {
  constexpr A a;
  [=]<typename T, int = a> {};
}

The only reason from the wording for why a is not odr-usable from the default argument (and thus not captured, see https://eel.is/c++draft/expr.prim.lambda.capture#7) is because there is a lambda scope. There is implementation divergence: https://godbolt.org/z/d41381qxM. Behaviours observed include accepting the code, rejecting the code because of the deleted copy constructor, and rejecting the code because a is named from the default template argument.

Suggested resolution:
Replace the bullets under https://eel.is/c++draft/basic.def.odr#10.2 with:

  • the intervening scope is a block scope,
  • the intervening scope is the function parameter scope of a lambda-expression, or
  • the intervening scope is the lambda scope of a lambda-expression that has a simple-capture naming the entity or has a capture-default, and the block scope of the lambda-expression is also an intervening scope.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions