首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >当右手是临时的时候,co_return和co_yield

当右手是临时的时候,co_return和co_yield
EN

Stack Overflow用户
提问于 2021-04-20 04:08:02
回答 1查看 909关注 0票数 2

背景:这个问题是在我阅读康科罗的来源,特别是这条线时提出的。

问题:考虑以下代码:

代码语言:javascript
复制
#include <coroutine>
#include <stdexcept>
#include <cassert>
#include <iostream>

struct result_type {
    result_type() {
        std::cout << "result_type()\n";
    }
    result_type(result_type&&) noexcept {
        std::cout << "result_type(result_type&&)\n";
    }
    ~result_type() {
        std::cout << "~result_type\n";
    }
};

struct task;

struct my_promise {
    using reference = result_type&&;
    result_type* result;

    std::suspend_always initial_suspend() { return {}; }

    std::suspend_always final_suspend() noexcept {
        return {};
    }

    task get_return_object();

    void return_value(reference result_ref) {
        result = &result_ref;
    }

    auto yield_value(reference result_ref) {
        result = &result_ref;
        return final_suspend();
    }

    void unhandled_exception() {}
};

struct task {
    std::coroutine_handle<my_promise> handle{};

    ~task() {
        if (handle) {
            handle.destroy();
        }
    }

    void run() {
        handle.resume();
    }

    my_promise::reference result() {
        return std::move(*handle.promise().result);
    }
};

task my_promise::get_return_object() {
    return { std::coroutine_handle<my_promise>::from_promise(*this) };
}

namespace std {
    template <>
    struct coroutine_traits<task> {
        using promise_type = my_promise;
    };
}

task f1() {
    co_return result_type{};
}

task f2() {
    co_yield result_type{};
    
    // silence "no return_void" warning. This should never hit.
    assert(false);
    co_return result_type{};
}

int main() {
    {
        std::cout << "with co_return:\n";
        auto t1 = f1();
        t1.run();
        auto result = t1.result();
    }

    std::cout << "\n==================\n\n";

    {
        std::cout << "with co_yield:\n";
        auto t2 = f2();
        t2.run();
        auto result = t2.result();
    }
}

在上述守则中:

  1. 调用f1()f2()都会启动一个协同线。立即挂起协同线,并将包含协同线句柄的task对象返回给调用方。coroutine的承诺包含result --指向result_type的指针。其目的是使它指向协同线的结果,当它完成。
  2. 对返回的任务调用task.run(),该任务将恢复存储的协同线句柄。
  3. 这里是f1f2分歧的地方:
代码语言:javascript
复制
- `f1` uses `co_return result_type{}` to invoke `return_value` on the promise. Note that `return_value` accepts an r-value reference of `result_type`, therefore bounding it to a temporary.
- `f2` uses `co_yield result_type{}` to invoke `yield_value` on the promise. Same as `return_value`, it accepts an r-value 
    - In addition, `yield_value` returns `final_suspend()`, which in turn returns `std::suspend_always`, instructing the coroutine to suspend after yielding the value.
代码语言:javascript
复制
- In both `return_value` and `yield_value`, `result` is set to point to the argument they received.

但是,由于final_suspend也在co_return之后被调用(以及它的结果等待着),所以我希望使用co_returnco_yield之间没有什么区别。然而,编译器证明我错了

代码语言:javascript
复制
with co_return:
result_type()
~result_type
result_type(result_type&&)
~result_type

==================

with co_yield:
result_type()
result_type(result_type&&)
~result_type
~result_type

注意,在上面的输出中,co_return版本构建一个结果,销毁它,然后从它中移动构造,调用未定义的行为。然而,co_yield版本似乎工作得很好,在中移除构造后,只破坏了结果。

为什么这里的行为不同?

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2021-04-20 04:46:46

您正在打破C++的一个基本规则:您编写了一个函数,该函数接受一个潜在的prvalue,并存储一个指针,该指针比给它们提供prvalue的函数调用更有效。实际上,每当您看到一个函数接受rvalue-引用(或const-lvalue-引用)并存储指向该对象的指针/引用(这将超过该函数)时,您应该认为该代码充其量是非常可疑的。

如果一个函数接受一个参数作为rvalue引用,这意味着您需要在该函数调用中使用它或从它移动。通常不期望Prvalue超过传递给它们的函数调用,所以要么使用它们,要么丢失它们。

在任何情况下,你所看到的行为都是你应该看到的。当协同线发出co_return时,它.退货。这意味着协同线块的主体已经退出。在块仍然存在时调用return_value,但是一旦这样做了,coroutine块及其所有自动变量(包括参数)就会消失。

这就是为什么从正常函数返回对自动变量的引用是个坏主意。对于co_return来说,这也是个坏主意,即使您间接地将该引用传递给调用方。

co_yield版本可以工作(您仍然不应该这么做,因为您不应该这样对待prvalue,但它是必需的),因为co_yield语句本身被yield_value的返回值告知要挂起。这保留了coroutine的堆栈,包括co_yield语句本身中的所有prvalue,直到coroutine恢复为止。

但是,您应该在yield_value函数中进行移动,就像通常对rvalue引用参数一样。

票数 4
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/67172316

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档