Мне интересно понять причину выбора дизайна, согласно которому std::coroutine_handle
фактически не является владельцем состояния/фрейма сопрограммы.
Я понимаю, что (по крайней мере, обычно?) тип возвращаемого значения сопрограммы отвечает за хранение std::coroutine_handle
и, следовательно, отвечает за время жизни состояния/кадра сопрограммы.
Но все же, в чем был бы недостаток, если бы std::coroutine_handle
обрабатывал состояние/кадр сопрограммы через std::unique_ptr
(который во время выполнения столь же эффективен, как и необработанный указатель, не так ли?)?
Неужели так ценна возможность копирования std::coroutine_handle
?
Когда вы await
, awaiter
получает доступ к сопрограмме через дескриптор сопрограммы. Он не владеет сопрограммой.
Таким образом, если бы мы добавили дескриптор владельца, нам все равно потребовался бы дескриптор, не являющийся владельцем.
Написание владеющего дескриптора сопрограммы
template<class T>
struct destroy_coroutine {
void operator()(auto&& handle)const {
handle.destroy();
}
using pointer=std::coroutine_handle<T>;
};
template<class T=void>
using unique_coroutine_handle = std::unique_ptr< std::coroutine_handle<T>, destroy_coroutine<T> >;
довольно короткий. (извиняюсь, если у меня выше опечатки; звонки на завтрак!)
Дескриптор, не являющийся владельцем, имеет смысл как часть низкоуровневого API сопрограммы. await_suspend
передается дескриптор ожидающей сопрограммы, но эта сопрограмма обычно фактически принадлежит возвращаемому объекту (например, std::generator
, предложенному std::task
и т. д.). Таким образом, по крайней мере один из этих двух дескрипторов не должен быть владельцем. Поэтому, если язык должен предоставлять только один тип дескриптора сопрограммы, он должен быть невладеющим.
Кроме того, вы можете легко создать тип дескриптора владеющей сопрограммы, который обертывает невладеющий тип. Но на практике это, как правило, не так уж полезно, поскольку ожидается, что пользователям редко придется напрямую взаимодействовать с дескрипторами сопрограмм. Вам нужно будет написать лишь небольшое количество возвращаемых типов сопрограммы, и, надеюсь, не составит большого труда не забыть написать для них деструктор и конструктор перемещения (и, возможно, оператор перемещения перемещения).
Вам разрешено получить множество различных дескрипторов из одного и того же обещания или даже ни одного. Концептуально это не означает, что «дескриптор владеет сопрограммой».