协程promise_type中return_value(Foo&&)接受左值参数的编译疑问
协程promise_type中return_value(Foo&&)接受左值参数的编译疑问
这个问题问得特别到位!我刚摸协程的时候,也被这个“看似不符合常规重载匹配规则”的细节搞懵过好一会儿,咱们一点点拆解来看:
首先先把你给出的核心代码片段整理出来,方便对照:
#include <print> struct Foo {}; struct Function { struct promise_type { Foo res_; Function get_return_object() { return {}; } std::suspend_never initial_suspend() { return {}; } std::suspend_never final_suspend() noexcept { return {}; } // 你定义的只接受右值引用的return_value void return_value(Foo &&foo) { std::println("return_value"); res_ = std::move(foo); } void unhandled_exception() {} }; Foo get() { return res_; } // 简化实现,仅作示意 }; int main() { auto coro = []()->Function { std::println("coroutine body"); Foo foo{}; co_return foo; // 这里的foo是左值,却能匹配右值引用参数? }; Function fun = coro(); Foo res = fun.get(); }
你疑惑的点本质是:按常规C++规则,左值foo根本没法直接绑定到右值引用参数Foo&&,为什么co_return foo能编译通过?
其实这不是GCC的特殊优化,而是C++协程标准里明确定义的行为——在co_return expr的语境下,编译器会自动帮你把expr转换成右值引用,相当于偷偷替你执行了promise.return_value(std::move(foo))。
为什么要这么设计?道理很简单:你代码里的foo是协程栈上的局部变量,在执行完co_return之后,这个协程的栈帧马上就会被销毁,foo的生命周期也就走到头了。此时对foo执行移动操作不仅完全安全,还能避免不必要的拷贝,符合C++“尽可能高效”的设计思路,所以标准就直接把这个操作标准化了,不用我们手动写std::move。
如果你还不信,可以给Foo加个移动构造函数验证一下:
struct Foo { Foo() = default; Foo(Foo&&) { std::println("Foo 移动构造函数被调用"); } };
运行代码后你会看到输出顺序是:
coroutine body return_value Foo 移动构造函数被调用
这就实锤了:foo确实被当成右值处理,触发了移动操作,而不是你担心的“左值直接绑定右值引用”的异常情况。
另外补充一句:这个规则是C++20协程标准的统一要求,不是GCC独有的,你换成Clang或者MSVC的最新版本编译,结果也是一样的。
内容来源于stack exchange
相关产品推荐
相关产品推荐

