"Subroutines (functions) are special cases of coroutines". -- from Wikipedia

When coding function template and fighting with distinct 'return' and 'co_return' keywords, I'm certain that function and coroutine are same in general more. But with diff wording I find it inconvenient and broken modern C++ template and generic programming idiom.

Say if I have a `max(a, b)` generic function, I need write two version with current coroutine design:


template <class T>

const T max (const T a, const T b) {

  return (a < b) ? b : a;

}


template <class T, class R>

const R max (const T a, const T b) {

  co_return (a < b) ? b : a;

}


auto x = max<int>(1, 2);

auto y = co_await max<int, std::lazy<double>>(3, 4);


But with P1485: "Better keywords for the Coroutines" and tweak a bit (use `async(bool)`, like `noexcept(bool)` does), I find just need write one:


template <class T, class R = T>

const R max (const T a, const T b) async(std::is_awaitable_v<R>) {

  return (a < b) ? b : a;

}


auto x = max<int>(1, 2);

auto y = await max<int, std::lazy<double>>(3, 4);


Modern C++ is centrally on template / generic programming and still making it more widely. But current coroutine design strangely broken it (and be accepted in c++20).

Looks like P1485 is not just perfection keywords but also defends on template / generic programming the modern C++ core value.

Maybe the `max(a, b)` example too simple. But if `async(false)` can ignore `await`'s async behaviour it can support more useful generic functions like this one (from P1485):


template <class Stream>

auto f(Stream str) -> std::future_if_awaitable_t<Stream, int> async(std::is_awaitable_v<Stream>) {

  vector<Stream::value_type> buf = ...;

  int count = await str.read(512, buf);

  return count + 11;

}


With this `f(Stream str)` generic function, it can support in-memory `std::stringstream` without async await well.

If we want protect C++ core value on template and generic programming, I suggest committee reconsider P1485R1 in c++20 or 23.

Thanks!

--
soplwang