Date: Thu, 02 Oct 2025 18:36:34 +0300
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.
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.
Received on 2025-10-02 15:36:41