简体   繁体   中英

VS2015 C++ coroutines: promise.get_return_object() return type and coroutine return type

I have a question about Visual Studio 2015 implementation of the Coroutines TS. P0057r5 working paper states that the coroutine behaves as if its body were:

{
    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) and that when a coroutine returns to its caller, the return value is produced as if by the statement return gro; (§ 8.4.4\\5)

Please note that result of p.get_return_object() is stored in variable with auto deduced type.

Let coroutine return type be A and promise.get_return_object() return type be B . According to P0057r5 mentioned above variable gro should have type B (deduced by auto ) and object of type A should be constructed from gro when coroutine returns to its caller (for example, using conversion operator in B or implicit constructor from B in A ).

In current Visual Studio implementation (compiler version string: "Microsoft (R) C/C++ Optimizing Compiler Version 19.00.24215.1 for x86") conversion is done after p.initial_suspend() and before F' gets called as if gro type was set to match return type of the coroutine ( A ) and not the return type of promise.get_return_object() ( B ).

Am I missing something or is this a bug?

Minimal example (compile with /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();
}

Output:

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

Capturing return value of get_return_object() by auto gro was added in p0057r5. In previous revision (p0057r4) this part reads as

...the coroutine behaves as if its body were:

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

and

When a coroutine returns to its caller, the return value is obtained by a call to p.get_return_- object(). A call to a get_return_object is sequenced before the call to initial_suspend and is invoked at most once.

VS2015 obviously implements older version of this paper.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM