поведение std :: async (std :: launch :: deferred) + std :: future :: then

Идея отложенного будущего (достигнутая только путем вызова std::async с std::launch::deferred) заключается в том, что обратный вызов вызывается только тогда, когда кто-то пытается подождать или вытащить футуристическое значение или исключение будущего. к тому времени обратный вызов не выполняется.

Что произойдет, если я присоединяю продолжение к отложенному будущему с помощью std::future::then? отложенное будущее теряется (then делает недействительным будущее), и вместо этого возвращается новое будущее.

В этом случае, согласно стандарту, что должно произойти? является ли новое будущее также отложенным будущим? это будет просто тупик? этот вопрос не рассматривается в последней документации.

Ответ 1

Это, на мой взгляд, кажется ошибкой в TS. Или, по крайней мере, недокументированная ловушка.

Вот текст из TS:

2.3 [futures.unique_future]/6-10

template <class F>
see below then(F&& func);

Требуется:

INVOKE(DECAY_COPY (std::forward<F>(func)), std::move(*this)) shall be a valid expression.

Последствия:

Функция создает общее состояние, связанное с возвращенным будущим объектом. Дополнительно,

Когда состояние общего состояния объекта готово, продолжение INVOKE(DECAY_COPY(std::forward<F>(func)), std::move(*this)) вызывается в неуказанном потоке выполнения с вызовом DECAY_COPY() оценивается в потоке, который тогда назывался.

Любое значение, возвращаемое из продолжения, сохраняется как результат в общем состоянии будущего. Любое исключение, распространяемое от выполнения продолжения, сохраняется как исключительный результат в общем состоянии будущего.

Возвращает:

Когда result_of_t<decay_t<F>(future<R>)> является future<R2>, для некоторого типа R2 функция возвращает future<R2>. В противном случае функция возвращает future<result_of_t<decay_t<F>(future<R>)>>. [Примечание: вышеприведенное правило называется неявным развертыванием. Без этого правила возвращаемый тип, а затем принятие вызываемого вызова, возвращающего future<R> был бы future<future<R>>. Это правило позволяет избежать таких вложенных будущих объектов. Тип f2 ниже - future<int> а не future<future<int>>:

[ Пример:

future<int> f1 = g();
future<int> f2 = f1.then([](future<int> f) {
                    future<int> f3 = h();
                    return f3;
                 });

- конец примера]

- конечная нота]

Постусловия:

valid() == false в исходном будущем. valid() == true в будущем, возвращенном с этого момента. [Примечание. В случае неявного развертывания срок действия будущего, возвращаемого thenfunc, не может быть установлен до завершения продолжения. Если это недопустимо, итоговое будущее будет готово за исключением типа std::future_error с условием ошибки std::future_errc::broken_promise. - конечная нота]

Специального случая для отложенной будущей задачи нет. Если эта отложенная будущая задача не готова до вызова. .then, у нее нет способа стать готовым, поэтому нет возможности вызывать разлагающуюся копию func.

Текст для shared_future похож; там, вы все равно можете заставить shared_future стать готовым после вызова. .then однако.

Если это предназначено; что .then на не готово отсроченном уникальное будущем приведет к возвращаемому значению future, которое никогда не может быть сделано готовым - это должно быть явными в TS/стандарте. Если это не предназначено, стандартный текст необходимо изменить.

Обратите внимание, что эти изменения не отображаются в черновом проекте N4762, опубликованном в 2018 году.

Я не уверен, как стандарт должен это исправить; что .then семантика разумно для shared_future, но не для future, и различающиеся семантика будет удивительно.