co_return vs. co_yield 当右手边是临时的

如何解决co_return vs. co_yield 当右手边是临时的

背景:当我阅读 cppcoro 的来源,特别是 this line 时,出现了这个问题。

问题:考虑以下代码:

#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 对象返回给调用者。协程的promise 包含result - 一个指向result_type指针。目的是让它在完成时指向协程的结果。

  2. task.run() 在返回的任务上被调用,它恢复存储的协程句柄。

  3. f1f2 的不同之处在于:

    • f1 使用 co_return result_type{} 对 Promise 调用 return_value。请注意,return_value 接受 result_type 的 r 值引用,因此将其绑定到临时值。
    • f2 使用 co_yield result_type{} 对 Promise 调用 yield_value。与 return_value 相同,它接受一个 r 值
      • 此外,yield_value 返回 final_suspend(),后者又返回 std::suspend_always,指示协程在产生值后挂起。
    • return_valueyield_value 中,result 都设置为指向它们收到的参数。

然而,由于 final_suspend 也在 co_return 之后被调用(并等待其结果),我预计使用 co_returnco_yield 之间没有区别。但是,compiler proved me wrong

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 版本似乎运行良好,仅在 移动构造之后破坏结果。

为什么这里的行为不同?

解决方法

您违反了 C++ 的基本规则:您编写了一个函数,该函数接受一个潜在的纯右值并存储一个指针,该指针比赋予它们一个纯右值的函数调用寿命更长。事实上,任何时候你看到一个函数接受一个右值引用(或一个常量左值引用)并存储一个指向该对象的指针/引用,该对象将超过该函数,你应该认为该代码高度 充其量是可疑的。

如果一个函数接受一个参数作为右值引用,这意味着您应该在该函数调用中使用它或从它移动。纯右值通常不会超过传递给它们的函数调用的寿命,因此您要么使用它们,要么丢失它们。

无论如何,您看到的行为正是您应该看到的。当协程发出 co_return 时,它...返回。这意味着协程块的主体已经退出return_value 在块仍然存在时被调用,但一旦调用完成,协程块及其所有自动变量(包括参数)就会消失。

这就是为什么从普通函数返回对自动变量的引用是个坏主意。这对 co_return 来说同样是个坏主意,即使您是间接引导对调用者的引用。

co_yield 版本有效(你仍然不应该这样做,因为这不是你应该如何对待纯右值,但它必须工作)因为 co_yield 语句本身被告知通过 yield_value 的返回值暂停。这会保留协程的堆栈,包括 co_yield 语句本身内的任何纯右值,直到协程恢复。

但同样,您应该在 yield_value 函数中进行移动,就像您通常对右值引用参数所做的那样。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


使用本地python环境可以成功执行 import pandas as pd import matplotlib.pyplot as plt # 设置字体 plt.rcParams[&#39;font.sans-serif&#39;] = [&#39;SimHei&#39;] # 能正确显示负号 p
错误1:Request method ‘DELETE‘ not supported 错误还原:controller层有一个接口,访问该接口时报错:Request method ‘DELETE‘ not supported 错误原因:没有接收到前端传入的参数,修改为如下 参考 错误2:cannot r
错误1:启动docker镜像时报错:Error response from daemon: driver failed programming external connectivity on endpoint quirky_allen 解决方法:重启docker -&gt; systemctl r
错误1:private field ‘xxx‘ is never assigned 按Altʾnter快捷键,选择第2项 参考:https://blog.csdn.net/shi_hong_fei_hei/article/details/88814070 错误2:启动时报错,不能找到主启动类 #
报错如下,通过源不能下载,最后警告pip需升级版本 Requirement already satisfied: pip in c:\users\ychen\appdata\local\programs\python\python310\lib\site-packages (22.0.4) Coll
错误1:maven打包报错 错误还原:使用maven打包项目时报错如下 [ERROR] Failed to execute goal org.apache.maven.plugins:maven-resources-plugin:3.2.0:resources (default-resources)
错误1:服务调用时报错 服务消费者模块assess通过openFeign调用服务提供者模块hires 如下为服务提供者模块hires的控制层接口 @RestController @RequestMapping(&quot;/hires&quot;) public class FeignControl
错误1:运行项目后报如下错误 解决方案 报错2:Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile (default-compile) on project sb 解决方案:在pom.
参考 错误原因 过滤器或拦截器在生效时,redisTemplate还没有注入 解决方案:在注入容器时就生效 @Component //项目运行时就注入Spring容器 public class RedisBean { @Resource private RedisTemplate&lt;String
使用vite构建项目报错 C:\Users\ychen\work&gt;npm init @vitejs/app @vitejs/create-app is deprecated, use npm init vite instead C:\Users\ychen\AppData\Local\npm-
参考1 参考2 解决方案 # 点击安装源 协议选择 http:// 路径填写 mirrors.aliyun.com/centos/8.3.2011/BaseOS/x86_64/os URL类型 软件库URL 其他路径 # 版本 7 mirrors.aliyun.com/centos/7/os/x86
报错1 [root@slave1 data_mocker]# kafka-console-consumer.sh --bootstrap-server slave1:9092 --topic topic_db [2023-12-19 18:31:12,770] WARN [Consumer clie
错误1 # 重写数据 hive (edu)&gt; insert overwrite table dwd_trade_cart_add_inc &gt; select data.id, &gt; data.user_id, &gt; data.course_id, &gt; date_format(
错误1 hive (edu)&gt; insert into huanhuan values(1,&#39;haoge&#39;); Query ID = root_20240110071417_fe1517ad-3607-41f4-bdcf-d00b98ac443e Total jobs = 1
报错1:执行到如下就不执行了,没有显示Successfully registered new MBean. [root@slave1 bin]# /usr/local/software/flume-1.9.0/bin/flume-ng agent -n a1 -c /usr/local/softwa
虚拟及没有启动任何服务器查看jps会显示jps,如果没有显示任何东西 [root@slave2 ~]# jps 9647 Jps 解决方案 # 进入/tmp查看 [root@slave1 dfs]# cd /tmp [root@slave1 tmp]# ll 总用量 48 drwxr-xr-x. 2
报错1 hive&gt; show databases; OK Failed with exception java.io.IOException:java.lang.RuntimeException: Error in configuring object Time taken: 0.474 se
报错1 [root@localhost ~]# vim -bash: vim: 未找到命令 安装vim yum -y install vim* # 查看是否安装成功 [root@hadoop01 hadoop]# rpm -qa |grep vim vim-X11-7.4.629-8.el7_9.x
修改hadoop配置 vi /usr/local/software/hadoop-2.9.2/etc/hadoop/yarn-site.xml # 添加如下 &lt;configuration&gt; &lt;property&gt; &lt;name&gt;yarn.nodemanager.res