Вместо return в корутине используется co_return, возвращающий результат. В этой заметке я хочу реализовать простую корутину с использованием co_return. Возможно, вам будет интересно: Хотя я уже излагал теорию, лежащую в основе корутин, мне хочется еще раз об этом написать. Мой рассказ прост и основан на личном опыте. C++20 не предоставляет конкретные корутины, вместо этого он предлагает структуру для их реализации. Эта платформа состоит из более чем 20 функций, некоторые из которых вы должны имплементировать, а другие могут быть переопределены. На основе этих функций компилятор генерирует два рабочих процесса, которые определяют поведение корутины. Упрощенно говоря, корутины в C++20 — это обоюдоострый меч. С одной стороны, они предоставляют вам огромные возможности, с другой — они довольно сложны для понимания. Я посвятил корутинам более 80 страниц в своей книге "C++20: Узнай подробности", и то еще не все объяснил. Из моего опыта, использование простых корутин и их модификация — это самый простой — возможно, единственный — способ понять их. Именно такого подхода я придерживаюсь в следующих статьях. Я представляю простые корутины и модифицирую их. Чтобы сделать рабочий процесс наглядным, в тексте содержится много комментариев и добавлено ровно столько теории, сколько необходимо для понимания внутренней сути корутин. Мои объяснения ни в коем случае не являются исчерпывающими и предназначены только для того, чтобы послужить отправной точкой в углублении ваших знаний о корутинах. Если функцию можно только вызвать и вернуться обратно, то корутину можно вызвать, приостановить и возобновить, а также уничтожить приостановленную корутину. Благодаря новым ключевым словам co_await и co_yield, C++20 расширяет выполнение функций C++ двумя новыми концепциями. С помощью co_await expression можно приостанавливать и возобновлять выполнение выражения. Если вы используете co_await expression в функции func, вызов auto getResult = func() не блокируется, если результат вызова функции func() недоступен. Вместо потребляющей ресурсы блокировки осуществляется экономящее ресурсы ожидание. Выражение co_yield позволяет реализовывать функции-генераторы. Генераторы — функции, которые возвращают новое значение с каждым последующим вызовом. Функция генератор является подобием потоков данных, из которых можно получать значения. Потоки данных могут быть бесконечными. Таким образом, данные концепции являются основополагающими для ленивых вычислений в C++. Кроме того, корутина не выполняет return своего результата, она выполняет co_return своего результата. В этом наглядном примере createFuture является корутиной, поскольку в ней используется одно из трех новых ключевых слов co_return, co_yield или co_await и она возвращает корутину MyFuture<int>. Что? Вот это зачастую озадачивало меня. Название корутина (coroutine) используется для двух сущностей. Позвольте мне ввести два новых термина. createFuture — это фабрика корутин, которая возвращает объект корутины fut, используемый для запроса результата: fut.get().  Этой теории должно быть достаточно. Давайте поговорим о co_return. Признаться, корутина в программе eagerFuture.cpp - это самая простая корутина, которую можно себе представить, но которая все же делает что-то значимое: она автоматически сохраняет результат своего вызова. MyFuture ведет себя как будущее, которое выполняется немедленно (см. "Асинхронные вызовы функций"). Вызов корутины createFuture (строка 1) возвращает будущее, а вызов fut.get (строка 2) забирает результат связанного промиса. Есть одно тонкое различие с будущим: возвращаемое значение корутины createFuture доступно после ее вызова. Из-за проблем связанных с жизненным циклом корутины, она управляется std::shared_ptr (строки 3 и 4). Корутина всегда использует std::suspend_never (строки 5 и 6) и, таким образом, не приостанавливается ни перед запуском, ни после. Это означает, что корутина немедленно выполняется, когда вызывается функция createFuture. Функция-член get_return_object (строка 7) возвращает хэндл к корутине и сохраняет его в локальной переменной. return_value (строка 8) хранит результат работы корутины, который был предоставлен co_return 2021 (строка 9). Клиент вызывает fut.get (строка 2) и использует будущее в качестве дескриптора к промису. Функция-член get окончательно возвращает результат клиенту (строка 10). Вы можете подумать, что не стоит тратить усилия на имплементацию корутины, которая будет вести себя так же, как функция. Вы правы! Однако эта простая корутина является идеальной отправной точкой для написания различных вариантов реализации фьючерсов. На этом этапе я должен добавить немного теории.  Когда вы используете co_yield, co_await или co_return в функции, то она становится корутиной, и компилятор преобразует тело функции в нечто эквивалентное следующим строкам. Знакомы ли вам названия этих функций? Правильно! Это функции-члены внутреннего класса promise_type. Вот шаги, которые компилятор выполняет при создании объекта корутины в качестве возвращаемого значения фабрики корутины createFuture. Сначала он создает объект промис (строка 1), вызывает его функцию-член initial_suspend (строка 2), выполняет тело фабрики корутины (строка 3) и, наконец, вызывает функцию-член final_suspend (строка 4). Обе функции-члены initial_suspend и final_suspend в программе eagerFuture.cpp возвращают предопределенные awaitables (ожидающие) элементы std::suspend_never.  Как следует из названия, этот awaitable (ожидаемый) объект никогда не приостанавливается и, следовательно, объект корутина также никогда не приостанавливается и ведет себя как обычная функция. Ожидание - это то, чего вы можете ждать. Оператору co_await требуется awaitable (возможность ожидания). В одном из следующих постов я напишу об awaitable и втором рабочем процессе awaiter. На основании этого упрощенного рабочего процесса обещания можно сделать вывод о том, какие функции-члены нужны промису (promise_type) как минимум: Конструктор по умолчанию initial_suspend final_suspend unhandled_exception Конечно, это далеко не полное объяснение, но, по крайней мере, достаточное для того, чтобы получить первое представление о рабочем процессе корутин. Возможно, вы уже догадались. В своем следующем посте я использую эту простую корутину в качестве отправной точки для дальнейших экспериментов. Во-первых, я добавлю комментарии к программе, чтобы сделать ее рабочий процесс явным, во-вторых, я сделаю корутину ленивой и возобновлю ее на другом потоке. Материал подготовлен в рамках курса «C++ Developer. Professional».  Всех желающих приглашаем на открытый урок «C++20: Обзор нововведений». Цели занятия: -  открыть для себя дивный мир последнего стандарта, о котором ходят столько легенд и слухов; -  объяснить, почему именно такие изменения были добавлены в стандарт; -  получить список нововведений для повседневного использования.   >> РЕГИСТРАЦИЯ Редактор