Home > Software engineering >  Why does template not discard the co_return?
Why does template not discard the co_return?

Time:03-11

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;
}
  • Related