C++协程promise_type条件返回return_void/return_value报错修复咨询
问题描述
尝试为模板化的promise_type结构体实现条件式的return_void(当模板参数Ret为void时)与return_value(当Ret为非void时),但两种实现方式均触发编译错误:
方式一:使用std::enable_if的错误实现
template <typename Ret> struct promise_type { auto get_return_object() {/*...*/} auto initial_suspend() {/*...*/} auto return_void() -> std::enable_if_t<std::is_void_v<Ret>> {/*...*/} auto return_value(Ret) -> std::enable_if_t<!std::is_void_v<Ret>> {/*...*/} void unhandled_exception() {/*...*/} auto final_suspend() noexcept {/*...*/} // Rest of promise_type's definition ... };
编译错误:
In file included from src/TestSyncTask.cpp:1: In file included from include/asyncxx/SyncTask.hpp:2: In file included from /usr/local/lib/gcc/x86_64-pc-linux-gnu/15.0.1/../../../../include/c++/15.0.1/coroutine:48: /usr/local/lib/gcc/x86_64-pc-linux-gnu/15.0.1/../../../../include/c++/15.0.1/type_traits:2837:44: error: no type named 'type' in 'std::enable_if<false>'; 'enable_if' cannot be used to disable this declaration 2837 | using enable_if_t = typename enable_if<_Cond, _Tp>::type; | ^~~~~ include/asyncxx/SyncTask.hpp:12:36: note: in instantiation of template type alias 'enable_if_t' requested here 12 | auto return_void() -> std::enable_if_t<std::is_void_v<Ret>> {} | ^ src/TestSyncTask.cpp:4:15: note: in instantiation of member class 'SyncTask<int>::promise_type' requested here 4 | SyncTask<int> async_main(const int argc, const char *const argv[]) { | ^ 1 error generated.
方式二:使用requires的错误实现
template <typename Ret> struct promise_type { auto get_return_object() {/*...*/} auto initial_suspend() {/*...*/} auto return_void() requires std::is_void_v<Ret> {/*...*/} auto return_value(Ret) requires (!std::is_void_v<Ret>) {/*...*/} void unhandled_exception() {/*...*/} auto final_suspend() noexcept {/*...*/} // Rest of promise_type's definition ... };
编译错误:
src/TestSyncTask.cpp:4:15: error: the coroutine promise type 'promise_type' declares both 'return_value' and 'return_void' 4 | SyncTask<int> async_main(const int argc, const char *const argv[]) { | ^ include/asyncxx/SyncTask.hpp:12:14: note: member 'return_void' first declared here 12 | auto return_void() requires std::is_void_v<Ret> {} | ^ include/asyncxx/SyncTask.hpp:13:14: note: member 'return_value' first declared here 13 | auto return_value(Ret val) requires (!std::is_void_v<Ret>) { | ^ 1 error generated.
错误原因分析
方式一错误原因:
std::enable_if的条件依赖于类模板参数Ret,当实例化类模板时,编译器会检查所有成员函数的声明,即使该函数不会被调用。例如当Ret=int时,return_void的enable_if条件为false,导致std::enable_if_t没有有效类型,触发编译错误。此时SFINAE无法生效,因为SFINAE只作用于函数模板的重载决议,而非类模板的成员声明。方式二错误原因:
C++协程标准要求,promise_type不能同时声明return_void和return_value,即使它们有互斥的requires约束。编译器在检查promise_type的合法性时,会无视约束条件,只要看到两个成员都存在就报错。
解决方案
方案一:类模板特化
通过特化promise_type分别处理Ret=void和非void的情况,这是最直接的方式:
// 基础模板:处理非void类型 template <typename Ret> struct promise_type { auto get_return_object() {/*...*/} auto initial_suspend() {/*...*/} auto return_value(Ret val) {/*...*/} void unhandled_exception() {/*...*/} auto final_suspend() noexcept {/*...*/} // 其他共享成员... }; // 特化版本:处理void类型 template <> struct promise_type<void> { auto get_return_object() {/*...*/} auto initial_suspend() {/*...*/} auto return_void() {/*...*/} void unhandled_exception() {/*...*/} auto final_suspend() noexcept {/*...*/} // 其他共享成员... };
方案二:将成员函数改为模板函数(利用SFINAE)
把return_void和return_value定义为模板成员函数,让enable_if的条件依赖于函数自身的模板参数,这样SFINAE会在重载决议时过滤掉不符合条件的函数:
template <typename Ret> struct promise_type { auto get_return_object() {/*...*/} auto initial_suspend() {/*...*/} template <typename T = Ret> auto return_void() -> std::enable_if_t<std::is_void_v<T>> {/*...*/} template <typename T = Ret> auto return_value(Ret val) -> std::enable_if_t<!std::is_void_v<T>> {/*...*/} void unhandled_exception() {/*...*/} auto final_suspend() noexcept {/*...*/} // 其他共享成员... };
这种方式的核心是让条件依赖于函数模板参数T,而不是类模板参数Ret,这样当实例化类模板后,调用成员函数时SFINAE会生效,自动选择符合条件的函数,同时编译器不会在类实例化时检查无效的函数声明。
方案三:使用C++20概念约束+继承
通过继承方式拆分return_void和return_value的实现,利用类型判断选择基类:
// 非void类型的基类 template <typename Ret> struct promise_return_value { auto return_value(Ret val) {/*...*/} }; // void类型的基类 struct promise_return_void { auto return_void() {/*...*/} }; template <typename Ret> struct promise_type : std::conditional_t<std::is_void_v<Ret>, promise_return_void, promise_return_value<Ret>> { auto get_return_object() {/*...*/} auto initial_suspend() {/*...*/} void unhandled_exception() {/*...*/} auto final_suspend() noexcept {/*...*/} // 其他共享成员... };
这种方式将不同的返回逻辑拆分到独立基类中,通过std::conditional_t根据Ret类型选择继承的基类,避免在同一个类中同时声明两个互斥的成员函数。
内容的提问来源于stack exchange,提问作者shynur

