简体   繁体   中英

`std::move` a `co_yield`ed result?

Say I have a std::vector that is declared in a loop's body and co_yield ed:

some_generator<std::vector<int>> vector_sequence() {
    while (condition()) {
        std::vector<int> result = get_a_vector();
        result.push_back(1);
        co_yield std::move(result); // or just: co_yield result; 
    }
}

Quite obviously, result isn't going to be used again after being co_yield ed (or I am horribly mistaken), so it would make sense to move it. I tried co_yield ing a simple non-copyable type without std::move and it did not compile, so in generic code, one would have use std::move . Does the compiler not recognize this (compiler bug?) or is it intended by the language that co_yield always copies an lvalue, so I have to std::move ? I know that return ing an lvalue that is a local variable issues a move or some other sort of copy elision, and this does not seem so much different from it.

I have read C++: should I explicitly use std::move() in a return statement to force a move? , which is related to this question, but does not answer it, and considered co_return vs. co_yield when the right hand side is a temporary , which as far as I understand, is not related to this question.

The implicit move rule ( [class.copy.elision]/3 ) applies to return and co_return statements and to throw expressions. It doesn't apply to co_yield .

The reason is that, in the contexts enumerated in [class.copy.elision]/3, the execution of the return or co_return statement or throw expression ensures that the implicitly movable entity's lifetime ends. For example,

auto foo() {
    std::string s = ...;
    if (bar()) {
        return s;
    }
    // return something else
}

Here, even though there is code after the return statement, it's guaranteed that if the return statement executes, then any code further down that can see s will not execute . This makes it safe to implicitly move s .

In contrast, co_yield only suspends the coroutine and does not end it in the manner of co_return . Thus, in general, after co_yield result; is evaluated, the coroutine might later resume and use the very same result variable again. This means that in general, it's not safe to implicitly transform the copy into a move; therefore, the standard does not prescribe such behaviour. If you want a move, write std::move .

If the language were to allow implicit move in your example, it would have to have specific rules to ensure that, although the variable could be used again after co_yield , it is in fact not. In your case, it might indeed be that the loop will immediately end and thus the result variable's lifetime will end before its value can be observed again, but in general you would have to specify a set of conditions under which this can be guaranteed to be the case. Then, you could propose that an implicit move occur only under those conditions.

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