使用co_await时不可拷贝/移动类型出现意外memcpy的问题求助
问题解答
这是编译器bug吗?
是,这属于ARM GCC 11.3的协程实现缺陷。C++标准明确要求:当类型的拷贝/移动构造函数被显式删除时,编译器不能通过memcpy这类字节复制操作来模拟拷贝或移动——哪怕该类型是trivially可移动的。GCC 13.1已修复此问题,证明这是旧版本的实现错误。
对拷贝消除的理解有误吗?
你的核心逻辑是对的:将awaitable设为不可拷贝/不可移动,就是为了强制编译器在目标位置直接构造对象(即触发拷贝消除),保证地址稳定。但GCC 11.3在处理co_await的prvalue操作数时,没有正确遵循这一规则,错误地生成了memcpy来复制不可移动的AwaitableBase成员。
依赖临时对象地址在co_await期间稳定是否属于UB?
不属于。根据C++标准,co_await表达式的awaitable对象生命周期会延续到await_resume()执行完成后。只要你直接构造临时awaitable作为co_await的操作数(而非通过拷贝/移动),其地址在整个等待过程中是稳定有效的——GCC 11.3的bug是例外情况。
针对GCC 11.3的解决方案
以下三种方法可解决该问题:
1. 给Awaitable添加显式构造函数,避免聚合初始化
将Awaitable从聚合类型改为带显式构造函数的类型,让编译器直接在协程帧中构造AwaitableBase成员,避免临时对象拷贝:
struct Awaitable{ AwaitableBase base; bool ready{false}; // 显式构造函数,直接初始化base explicit Awaitable(AwaitableBase) : base() {} // 若需传递参数给AwaitableBase,可改为: // explicit Awaitable(Params&& params) : base(std::forward<Params>(params)) {} bool await_ready() {return false;} void await_suspend(std::coroutine_handle<task::promise_type> handle) { handle.promise().ready_ptr = &ready; } int await_resume() { return 2; } };
2. 在协程中声明局部awaitable变量
将临时awaitable改为协程帧内的局部变量,确保地址稳定且无拷贝:
task example() { Awaitable a{make_awaitable_base()}; co_await a; }
此方式下a直接分配在协程帧中,co_await直接使用其地址,完全规避临时对象拷贝问题。
3. 调整make_awaitable_base的返回类型
若允许,让工厂函数直接返回Awaitable而非AwaitableBase,减少一层包装的构造开销:
Awaitable make_awaitable() { return Awaitable{}; } task example() { co_await make_awaitable(); }
内容的提问来源于stack exchange,提问作者Lukas Lang
相关产品推荐
相关产品推荐

