Почему шаблон не отбрасывает co_return?

Я хотел бы сделать функцию как с синхронизацией, так и с версией сопрограммы, без использования специализации шаблона, т.е. с if constexpr.

Это функция, которую я написал:

      template <Async _a>
AsyncResult<int, _a> func(int a) {
  if constexpr (_a == Async::Disable)
    return a;
  else
    co_return a;
}

Но когда я создаю истинную ветку, она выдает ошибку

      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

Почему это не работает?

Полный код с реализацией типа обещания

2 ответа

Стандарт прямо говорит, что это невозможно. Согласно примечанию 1 в stmt.return.coroutine#1

... Сопрограмма не должна содержать оператор возврата ([stmt.return]).

[Примечание 1: Для этого определения не имеет значения, заключен ли оператор return в оператор отбрасывания ([stmt.if]). — примечание в конце]

Таким образом, вы не сможете вернуться из сопрограммы, даже если она находится в отброшенном операторе. Вы можете специализировать шаблон функции вместо использования 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;
}

Вот демо .

Функция, которая имеет co_return/ co_await/ co_yieldоператор в нем безоговорочно является сопрограммой, даже если он отбрасывается if constexpr.

У вас должно быть 2 разные функции. Вот некоторые вещи, которые вы могли бы сделать:

      // 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;
}
Другие вопросы по тегам