I am trying to define my own coroutine promise type, and I have the following declarations:
void return_void() requires std::same_as<T, void> {}template<class X>void return_value(X&& x) requires (!std::same_as<T, void>) {}
But, the compiler complains that I have defined both, despite the requires
declarations:
return.cpp: In function ‘Future<void> b()’:return.cpp:103:40: error: the coroutine promise type ‘std::__n4861::__coroutine_traits_impl<Future<void>, void>::promise_type’ {aka ‘Future<void>::promise_type’} declares both ‘return_value’ and ‘return_void’ 103 | __attribute__((noinline)) Future<void> b() | ^return.cpp:27:14: note: ‘return_void’ declared here 27 | void return_void() requires std::same_as<T, void> {} | ^~~~~~~~~~~return.cpp:30:14: note: ‘return_value’ first declared here 30 | void return_value(X&& x) requires (!std::same_as<T, void>) {} | ^~~~~~~~~~~~
Isn't the point of the requires
feature to be able to express overloads like this without std::enable_if
trickery and inferred types?
Full minimal example:
#include <concepts>#include <coroutine>#include <exception>#include <iostream>#include <vector>template<class T>class Future{public: class Awaiter; class promise_type { private: std::exception_ptr _exception; std::coroutine_handle<> _continuation; friend class Awaiter; public: Future<T> get_return_object() { return {HandleT::from_promise(*this)}; } std::suspend_always initial_suspend() { return {}; } std::suspend_never final_suspend() noexcept { return {}; } void return_void() requires std::same_as<T, void> {} template<class X> void return_value(X&& x) requires (!std::same_as<T, void>) {} void unhandled_exception() { _exception = std::current_exception(); } }; operator bool() const { return _status == Status::Done || _status == Status::Exception; } struct Awaiter { private: Future<T>& future; public: explicit Awaiter(Future<T>& future) : future(future) {} // this will check if the future is already done, e.g. if it // was `co_await`ed previously bool await_ready() const noexcept { return bool(future); } // this coroutine is the one doing the `co_await` std::coroutine_handle<> await_suspend(std::coroutine_handle<> handle) noexcept { // remember we want to resume back intot his caller later future._handle.promise()._continuation = handle; // For now let the coroutine associated with this future // run, via "symmetric transfer", which takes the caller // resume call off the stack and replaces it with this // coroutine. return future._handle; } void await_resume() const noexcept { } }; auto operator co_await() { return Awaiter(*this); } void resume() { _handle.resume(); }protected: using HandleT = std::coroutine_handle<promise_type>; Future(HandleT&& p) : _handle(std::move(p)) {} Future(const Future&) = delete; Future& operator=(const Future&) = delete; friend class Awaiter; enum class Status { Unfinished, Done, Exception, Empty } _status = Status::Unfinished; HandleT _handle; std::exception_ptr _exception = nullptr;};Future<void> b(){ co_return;}