Search code examples
c++lambdacoroutine

Are coroutine lambda arguments forwarded to the promise type's constructor?


I'm writing an application where coroutines are executed by a scheduler. The scheduler includes a stack and some other functionality which is required for the coroutines to run properly; it doesn't make sense to have a coroutine which doesn't have access to the scheduler. I decided to make the scheduler a parameter of the promise type constructor so that whenever a coroutine is created, the promise registers it with the scheduler. Here's a simplification of my code:

#include <coroutine>

struct Scheduler {};

struct ReturnObject {
    
    struct promise_type;
    
};

struct ReturnObject::promise_type {
    
    promise_type(Scheduler &s) {
        // Register coroutine handle with s
    }
    
    ReturnObject get_return_object() {
        return {};
    }
    
    std::suspend_always initial_suspend() noexcept {
        return {};
    }
    
    std::suspend_always final_suspend() noexcept {
        return {};
    }
    
    void return_void() {}
    void unhandled_exception() {}
    
};

With these definitions, I can declare a coroutine and use it as follows:

ReturnObject procedure(Scheduler &) {
    co_return;
}

int main() {
    Scheduler s;
    auto p = procedure(s);
    return 0;
}

The above compiles and runs without issue. However, if I replace the function with an equivalent lambda:

int main() {
    Scheduler s;
    auto procedure = [](Scheduler &) -> ReturnObject { co_return; };
    auto p = procedure(s);
    return 0;
}

I get this error:

demo.cpp: In lambda function:
demo.cpp:36:67: error: no matching function for call to 'ReturnObject::promise_type::promise_type()'
   36 |     auto procedure = [](Scheduler &) -> ReturnObject { co_return; };
      |                                                                   ^
demo.cpp:13:5: note: candidate: 'ReturnObject::promise_type::promise_type(Scheduler&)'
   13 |     promise_type(Scheduler &s) {
      |     ^~~~~~~~~~~~
demo.cpp:13:5: note:   candidate expects 1 argument, 0 provided
demo.cpp:11:22: note: candidate: 'constexpr ReturnObject::promise_type::promise_type(const ReturnObject::promise_type&)'
   11 | struct ReturnObject::promise_type {
      |                      ^~~~~~~~~~~~
demo.cpp:11:22: note:   candidate expects 1 argument, 0 provided
demo.cpp:11:22: note: candidate: 'constexpr ReturnObject::promise_type::promise_type(ReturnObject::promise_type&&)'
demo.cpp:11:22: note:   candidate expects 1 argument, 0 provided

Why is this? The error indicates that the promise type constructor is being called with no arguments. However, I believe that the lambda's arguments should be forwarded to the promise type constructor as the function's are.


Solution

  • The lambda is not equivalent because its operator() is a non-static member function. An attempt is made to construct the promise from the argument list augmented with the implicit object argument; the error comes from attempting to default-construct it when that overload resolution fails.

    Note that converting the lambda to a function pointer will not help: it just creates a default-initialized closure object for you. In C++23, you can declare a lambda static to make it truly equivalent.