I'd like to make a function with both sync and coroutine version, without using template specialization, i.e. with an if constexpr
.
This is the function I wrote:
template <Async _a>
AsyncResult<int, _a> func(int a) {
if constexpr (_a == Async::Disable)
return a;
else
co_return a;
}
But when I instantiate the true branch it gives an error
auto a = func<Async::Disable>(1); // compiler error
auto b = func<Async::Enable>(2); // ok
error: unable to find the promise type for this coroutine
Why is this not working?
Full code with an implementation of the promise type
CodePudding user response:
The standard explicitly says this is not possible. As per Note 1 in stmt.return.coroutine#1
... A coroutine shall not enclose a return statement ([stmt.return]).
[Note 1: For this determination, it is irrelevant whether the return statement is enclosed by a discarded statement ([stmt.if]). — end note]
So you won't be able to return from a coroutine even if it's in a discarded statement. You can specialize the function template instead of using if constexpr
.
template <Async _a>
AsyncResult<int, _a> func(int a)
{
co_return a;
}
template <>
AsyncResult<int, Async::Disable> func<Async::Disable>(int a)
{
return a;
}
Here's a demo.
CodePudding user response:
A function which has a co_return
/co_await
/co_yield
statement in it is unconditionally a coroutine, even if it is discarded by if constexpr
.
You will have to have 2 different functions. Here are some things you could do:
// Have the second function be a lambda:
template <Async _a>
AsyncResult<int, _a> func(int a) {
if constexpr (_a == Async::Disable)
return a;
else
return ([](int a) -> AsyncResult<int, _a> {
co_return a;
})(a);
}
// Have the second function be a helper function
namespace detail {
AsyncResult<int, Async::Enable> async_enable_func(int a) {
co_return a;
}
}
template <Async _a>
AsyncResult<int, _a> func(int a) {
if constexpr (_a == Async::Disable)
return a;
else
return detail::async_enable_func(a);
}
// Have the second function be an overload
template <Async _a> requires (_a == Async::Disable)
AsyncResult<int, _a> func(int a) {
return a;
}
template <Async _a> requires (_a == Async::Enable)
AsyncResult<int, _a> func(int a) {
co_return a;
}
// Since you only have 1 template parameter, you can fully specialize
template<Async _a>
AsyncResult<int, _a> func(int a) {
return a;
}
template<>
AsyncResult<int, Async::Enable> func<Async::Enable>(int a) {
co_return a;
}