为什么std::forward在lambda函数体中失效?GCC与Clang谁符合标准?
问题复现代码
#include <utility> void f(auto const& fn1) { { auto fn2 = std::forward<decltype(fn1)>(fn1); auto fn3 = std::forward<decltype(fn2)>(fn2); // ok fn3(); } [fn2 = std::forward<decltype(fn1)>(fn1)] { auto const fn3 = fn2; auto fn4 = std::forward<decltype(fn3)>(fn3); // ok fn4(); auto fn5 = std::forward<decltype(fn2)>(fn2); // error fn5(); }(); } int main() { f([] {}); }
编译差异原因
核心分歧来自C++标准对lambda捕获变量的decltype规则、以及std::forward的语义约束:
- 调用
f([] {})时,fn1的推导类型是const Lambda&,其中Lambda是传入的无状态lambda的原生类型,无const修饰。 - 捕获初始化时,
std::forward<decltype(fn1)>(fn1)返回const Lambda&左值,复制捕获的fn2按auto推导规则得到类型Lambda(值类型,auto会丢弃顶层const)。 - C++标准明确规定:
decltype作用于lambda捕获的变量时,取捕获成员的静态类型,和lambda函数体内访问时的cv限定无关,因此decltype(fn2)的结果是Lambda。 - lambda的
operator()默认是const修饰的,在函数体内访问成员fn2时,fn2的表达式类型是const Lambda&左值。 std::forward<Lambda>(fn2)的两个重载签名分别是Lambda&& forward(Lambda&)和Lambda&& forward(Lambda&&),传入的const Lambda&左值无法匹配任意一个重载的参数类型,因此理论上应该触发编译错误。
标准符合判定
Clang的实现完全符合C++标准,GCC属于非标准的放宽处理:
GCC对无状态lambda做了特殊优化,由于无状态lambda没有非静态数据成员,拷贝和移动没有本质差异,因此GCC跳过了参数的const类型检查。如果将传入的lambda改为有状态(比如捕获一个int变量),GCC同样会触发编译错误,和Clang行为一致。
内容的提问来源于stack exchange,提问作者xmllmx
相关产品推荐
相关产品推荐

