Date: Thu, 02 Oct 2025 21:46:06 +0300
On Thu, 2025-10-02 at 18:36 +0300, Avi Kivity wrote:
> On Wed, 2025-10-01 at 10:19 -0400, Arthur O'Dwyer via Std-Proposals
> wrote:
> > On Wed, Oct 1, 2025 at 2:44 AM Rhidian De Wit
> > <rhidiandewit_at_[hidden]> wrote:
> > > Hi Arthur,
> > >
> > > Thanks for the response, I'll take a look at making a PR in the
> > > Core Guidelines to clarify the example a bit further.
> > >
> > > I wasn't very familiar yet with the explicit parameter object,
> > > especially not in lambdas, and after reading a bit about I did
> > > find that you can refer to the captured variables in a lambda by
> > > using the `self` parameter you
> > > declare: https://godbolt.org/z/9d1oPv9fG
> > > Now, this is pretty painful syntax, as we'd constantly have to
> > > type `std::forward_like<decltype(self)>(value)` to be able to use
> > > our values safely.
> > >
> >
> >
> > The good news: you don't have to, and should not, use
> > `std::forward_like<decltype(self)>`, because `self` is not a
> > forwarding reference. Never forward things that aren't forwarding
> > references!
> > Because `decltype(self)` is a prvalue type (namely `Lambda`),
> > therefore `std::forward<decltype(self)>(...)` expands to
> > `static_cast<Lambda&&>(...)`, and
> > `std::forward_like<decltype(self)>(...)` expands to basically
> > `std::move(...)`.
> >
> > The better news: Hm, you seem to have discovered that lambdas with
> > explicit `this` parameters already work the way I was hoping they
> > could be made to!
> > auto lambda = [x=2](this auto self) { return &x; };
> > int *px = lambda(); // return a (dangling) pointer into
> > `self`'s captures, not a pointer into `lambda`'s captures!
> > Godbolt: https://godbolt.org/z/Gve3frTh3
> >
> > Looks like this comes from
> > https://eel.is/c++draft/expr.prim.id#unqual-4.sentence-1 (says what
> > type an expression like `x` has, in terms of the type of `self`;
> > but not what value it has)
> > https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2021/p0847r7.html#wording-in-expr
> >
> > I can't find where this is normatively specified; the closest I've
> > gotten so far is
> > https://eel.is/c++draft/expr.prim.lambda#capture-11
> > "Every id-expression within the compound-statement of a lambda-
> > expression [...] is transformed into an access to the corresponding
> > unnamed data member of the closure type" — but this seems like a
> > category error to me; it should be saying something about the
> > corresponding unnamed data member of some particular object (not
> > some type), and in our example above, we need to ensure that that
> > particular object is specifically the by-value parameter `self` and
> > not some other object. And:
> > "the type of [an expression such as `x`] is the type of a class
> > member access expression naming the non-static data member that
> > would be declared for such a capture in the object parameter of the
> > function call operator of E" — but this describes only the
> > expression's type; it doesn't specify how to compute the
> > expression's value.
> >
> > But, on the assumption that it's supposed to work this way — and
> > all three compiler vendors seem to agree it is —
> > it looks like your problem can be solved by simply adding `(this
> > auto)` to your coroutine lambdas' signatures. (You don't need to,
> > and therefore probably shouldn't, give that parameter any name.) It
> > might even be worth creating a macro something like
> > #define DEFERRED this auto
> > so such lambdas really stand out:
> > int x = 1;
> > auto lambda = [&](DEFERRED, int y) -> Deferred { co_return x + y;
> > };
> > co_await lambda();
>
>
> This is incredibly helpful. In Seastar it's termed the "lambda
> coroutine fiasco" and there are bad workarounds for it, but this
> seems to exactly solve the problem.
>
>
Here it is in action: https://github.com/scylladb/seastar/pull/3016
> On Wed, 2025-10-01 at 10:19 -0400, Arthur O'Dwyer via Std-Proposals
> wrote:
> > On Wed, Oct 1, 2025 at 2:44 AM Rhidian De Wit
> > <rhidiandewit_at_[hidden]> wrote:
> > > Hi Arthur,
> > >
> > > Thanks for the response, I'll take a look at making a PR in the
> > > Core Guidelines to clarify the example a bit further.
> > >
> > > I wasn't very familiar yet with the explicit parameter object,
> > > especially not in lambdas, and after reading a bit about I did
> > > find that you can refer to the captured variables in a lambda by
> > > using the `self` parameter you
> > > declare: https://godbolt.org/z/9d1oPv9fG
> > > Now, this is pretty painful syntax, as we'd constantly have to
> > > type `std::forward_like<decltype(self)>(value)` to be able to use
> > > our values safely.
> > >
> >
> >
> > The good news: you don't have to, and should not, use
> > `std::forward_like<decltype(self)>`, because `self` is not a
> > forwarding reference. Never forward things that aren't forwarding
> > references!
> > Because `decltype(self)` is a prvalue type (namely `Lambda`),
> > therefore `std::forward<decltype(self)>(...)` expands to
> > `static_cast<Lambda&&>(...)`, and
> > `std::forward_like<decltype(self)>(...)` expands to basically
> > `std::move(...)`.
> >
> > The better news: Hm, you seem to have discovered that lambdas with
> > explicit `this` parameters already work the way I was hoping they
> > could be made to!
> > auto lambda = [x=2](this auto self) { return &x; };
> > int *px = lambda(); // return a (dangling) pointer into
> > `self`'s captures, not a pointer into `lambda`'s captures!
> > Godbolt: https://godbolt.org/z/Gve3frTh3
> >
> > Looks like this comes from
> > https://eel.is/c++draft/expr.prim.id#unqual-4.sentence-1 (says what
> > type an expression like `x` has, in terms of the type of `self`;
> > but not what value it has)
> > https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2021/p0847r7.html#wording-in-expr
> >
> > I can't find where this is normatively specified; the closest I've
> > gotten so far is
> > https://eel.is/c++draft/expr.prim.lambda#capture-11
> > "Every id-expression within the compound-statement of a lambda-
> > expression [...] is transformed into an access to the corresponding
> > unnamed data member of the closure type" — but this seems like a
> > category error to me; it should be saying something about the
> > corresponding unnamed data member of some particular object (not
> > some type), and in our example above, we need to ensure that that
> > particular object is specifically the by-value parameter `self` and
> > not some other object. And:
> > "the type of [an expression such as `x`] is the type of a class
> > member access expression naming the non-static data member that
> > would be declared for such a capture in the object parameter of the
> > function call operator of E" — but this describes only the
> > expression's type; it doesn't specify how to compute the
> > expression's value.
> >
> > But, on the assumption that it's supposed to work this way — and
> > all three compiler vendors seem to agree it is —
> > it looks like your problem can be solved by simply adding `(this
> > auto)` to your coroutine lambdas' signatures. (You don't need to,
> > and therefore probably shouldn't, give that parameter any name.) It
> > might even be worth creating a macro something like
> > #define DEFERRED this auto
> > so such lambdas really stand out:
> > int x = 1;
> > auto lambda = [&](DEFERRED, int y) -> Deferred { co_return x + y;
> > };
> > co_await lambda();
>
>
> This is incredibly helpful. In Seastar it's termed the "lambda
> coroutine fiasco" and there are bad workarounds for it, but this
> seems to exactly solve the problem.
>
>
Here it is in action: https://github.com/scylladb/seastar/pull/3016
Received on 2025-10-02 18:46:14