C++20:requires子句中使用立即调用lambda为何遭编译器拒绝?
为何三款编译器均拒绝调用
f2?C++20 requires子句中使用立即调用lambda是否违反标准? 问题代码
// 注意:`g`的函数体与`f2`的requires子句中的lambda体完全相同 constexpr auto g(auto b) { using c1 = decltype([](int){ return true; }); using c2 = decltype([](bool){ return true; }); struct v : c1, c2 { using c1::operator(), c2::operator(); }; return v{}(decltype(b){}); } auto f1(auto a) requires (g(decltype(a){})) {} auto f2(auto a) requires ([](auto b) { using c1 = decltype([](int){ return true; }); using c2 = decltype([](bool){ return true; }); struct v : c1, c2 { using c1::operator(), c2::operator(); }; return v{}(decltype(b){}); }(decltype(a){})) {} auto m() { f1(true); // 所有编译器均通过 f2(true); // 所有编译器均拒绝 }
编译器错误信息
Clang错误输出
<source>:12:5: error: 找不到匹配的'f2'调用 12 | f2(true); | ^~ <source>:9:6: note: 候选模板被忽略:约束未满足 [with a:auto = bool] 9 | auto f2(auto a) requires ([](auto b){ body }(decltype(a){})){} | ^ <source>:9:27: note: 原因是替换后的约束表达式格式错误: 找不到匹配的函数 用于调用类型为'(lambda at <source>:9:27)'的对象 9 | auto f2(auto a) requires ([](auto b){ body }(decltype(a){})){} | ^
GCC错误输出
<source>: 在'struct<lambda(auto:4)>::v'的实例化中: <source>:9:39: 要求来自'<lambda(auto:4)> [with auto:4 = bool]' 4 | struct v : c1, c2 { using c1::operator(), c2::operator(); }; \ | ^ <source>:9:45: 由'template<class auto:3> auto f2(auto:3) requires (<lambda>)({}) [with auto:3 = bool]'的替换所要求 9 | auto f2(auto a) requires ([](auto b){ body }(decltype(a){})){} | ~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~ <source>:12:7: 要求来自此处 12 | f2(true); | ~~^~~~~~ <source>:4:44: error: 类型'<lambda(auto:4)>::v::<lambda(int)>'不是'<lambda(auto:4)>::v'的基类型 4 | struct v : c1, c2 { using c1::operator(), c2::operator(); }; \ | ^ <source>:9:39: note: 在宏'body'的展开中 9 | auto f2(auto a) requires ([](auto b){ body }(decltype(a){})){} | ^~~~ <source>:4:60: error: 类型'<lambda(auto:4)>::v::<lambda(bool)>'不是'<lambda(auto:4)>::v'的基类型 4 | struct v : c1, c2 { using c1::operator(), c2::operator(); }; \ | ^ <source>:9:39: note: 在宏'body'的展开中 9 | auto f2(auto a) requires ([](auto b){ body }(decltype(a){})){} | ^~~~ <source>: 在'<lambda(auto:4)> [with auto:4 = bool]'的实例化中: <source>:9:45: 由'template<class auto:3> auto f2(auto:3) requires (<lambda>)({}) [with auto:3 = bool]'的替换所要求 9 | auto f2(auto a) requires ([](auto b){ body }(decltype(a){})){} | ~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~ <source>:12:7: 要求来自此处 12 | f2(true); | ~~^~~~~~ <source>:5:15: error: 对成员'operator()'的请求不明确 5 | return v{}(decltype(b){}); | ~~~^~~~~~~~~~~~~~~ <source>:9:39: note: 在宏'body'的展开中 9 | auto f2(auto a) requires ([](auto b){ body }(decltype(a){})){} | ^~~~ <source>:3:25: note: 候选者为:'<lambda(auto:4)>::<lambda(bool)>' 3 | using c2 = decltype([](bool){ return true; }); \ | ^ <source>:9:39: note: 在宏'body'的展开中 9 | auto f2(auto a) requires ([](auto b){ body }(decltype(a){})){} | ^~~~ <source>:2:25: note: '<lambda(auto:4)>::<lambda(int)>' 2 | using c1 = decltype([](int){ return true; }); \ | ^ <source>:9:39: note: 在宏'body'的展开中 9 | auto f2(auto a) requires ([](auto b){ body }(decltype(a){})){} | ^~~~ <source>: 在函数'auto m()'中: <source>:12:7: error: 找不到匹配的'f2(bool)'调用 12 | f2(true); | ~~^~~~~~ <source>:9:6: note: 候选者:'template<class auto:3> auto f2(auto:3) requires (<lambda>)({})' 9 | auto f2(auto a) requires ([](auto b){ body }(decltype(a){})){} | ^~ <source>:9:6: note: 推导的模板参数替换导致上述错误
原因分析与结论
核心问题
代码逻辑上f1和f2的约束判断逻辑完全一致,但f2被拒绝的原因在于:
当在requires子句的立即调用泛型lambda内部定义本地类型v,并继承该lambda内部的嵌套无捕获闭包类型(c1、c2),同时用using声明引入基类的operator()时,编译器在模板参数替换过程中无法正确解析嵌套闭包类型的作用域,导致using声明失效——编译器错误地将闭包类型的operator()视为v的内部成员而非基类成员,最终使得v{}(...)的调用找不到匹配的重载,约束表达式求值失败。
是否违反标准?
不违反C++20标准规则:
- C++20允许在requires子句中使用立即调用lambda表达式,约束表达式本身支持这种形式;
- 无捕获的闭包类型可以作为基类使用;
- 常量表达式上下文允许定义本地类型并进行继承、重载引入等操作。
三款编译器均拒绝f2属于实现层面的缺陷,而非代码违反标准。
内容的提问来源于stack exchange,提问作者303
相关产品推荐
相关产品推荐

