Сопрограммы C ++ VS2015: тип возврата обещания.get_return_object () и тип возврата сопрограммы

У меня вопрос о реализации Coroutines TS в Visual Studio 2015. В рабочем документе P0057r5 говорится, что сопрограмма ведет себя так, как если бы его тело было:

{
    P p;
    auto gro = p.get_return_object();
    co_await p.initial_suspend(); // initial suspend point
    F’
final_suspend:
    co_await p.final_suspend(); // final suspend point
}

(§ 8.4.4 \ 3) и что, когда сопрограмма возвращается к своей вызывающей стороне, возвращаемое значение создается, как если бы оператором return gro; (§ 8.4.4 \ 5)

Обратите внимание, что результат p.get_return_object() сохраняется в переменной с выведенным типом auto.

Пусть тип возврата сопрограммы будет A, а тип возврата promise.get_return_object() будет B. Согласно P0057r5, упомянутая выше переменная gro должна иметь тип B (выводится auto), а объект типа A должен быть построен из gro, когда сопрограмма возвращается вызывающей стороне (например, с использованием оператора преобразования в B или неявного конструктора из B в A).

В текущей реализации Visual Studio (строка версии компилятора: «Оптимизирующий компилятор Microsoft (R) C / C ++ версии 19.00.24215.1 для x86») преобразование выполняется после p.initial_suspend() и до вызова F', как если бы тип gro был установлен в соответствии с типом возврата сопрограмма (A), а не тип возврата promise.get_return_object() (B).

Я что-то упустил или это ошибка?

Минимальный пример (компилируйте с / await):

#include <experimental/coroutine>
#include <iostream>

struct foo;
struct foo_builder {
    foo_builder() {
        std::cout << "foo_builder constructor\n";
    }
    operator foo();
};

struct foo_promise {
    foo_builder get_return_object() {
        return{};
    }
    void return_value(int value) {
        std::cout << "co_return with " << value << "\n";
    }
    std::experimental::suspend_never initial_suspend() {
        std::cout << "Initial suspend\n";
        return{};
    }
    std::experimental::suspend_never final_suspend() {
        return{};
    }
};

struct foo {
    foo() {
        std::cout << "foo constructor\n";
    }
    using promise_type = foo_promise;
};

foo_builder::operator foo() {
    std::cout << "foo_builder conversion to foo\n";
    return{};
}

foo coroutine() {
    co_return 5;
}

foo simple() {
    foo_promise p;
    auto gro = p.get_return_object();
    // co_await p.initial_suspend(); // initial suspend point

    // co_return 5;
    p.return_value(5); //S;
    goto final_suspend;

final_suspend:
    // co_await p.final_suspend(); // final suspend point
    return gro;
}


int main() {
    auto v = coroutine();

    std::cout << "\nregular function:\n";

    auto v2 = simple();

    std::cin.ignore();
}

Выход:

Initial suspend
foo_builder constructor
foo_builder conversion to foo
foo constructor
co_return with 5

regular function:
foo_builder constructor
co_return with 5
foo_builder conversion to foo
foo constructor

person Serikov    schedule 18.10.2016    source источник


Ответы (1)


В p0057r5 был добавлен захват возвращаемого значения get_return_object() пользователем auto gro. В предыдущей версии (p0057r4) эта часть читается как

... сопрограмма ведет себя так, как если бы ее тело было:

{
    P p ;
    co_await p .initial_suspend(); // initial suspend point
    F’
final_suspend :
    co_await p .final_suspend(); // final suspend point
}

а также

Когда сопрограмма возвращается к своей вызывающей стороне, возвращаемое значение получается путем вызова p.get_return_- object (). Вызов get_return_object упорядочивается перед вызовом initial_suspend и вызывается не более одного раза.

VS2015, очевидно, реализует старую версию этого документа.

person Serikov    schedule 19.10.2016