C++万能引用作入参时std::is_enum判断失效问题
万能引用版本判断枚举失效的根本原因
问题和万能引用能不能同时绑定左值、右值无关——你写的T&& value确实是标准的转发引用(即常说的万能引用),绑定逻辑完全正常,失效的核心原因是你忽略了转发引用的模板类型推导规则,直接把带引用修饰的T传入了is_enum做判断。
转发引用的类型推导规则
当形参是无cv(const/volatile)修饰的T&&形式、且T是当前函数的模板参数时,类型推导遵循引用折叠规则:
- 传入左值表达式时,T会被推导为 表达式类型的左值引用,最终形参类型
T&&折叠为对应左值引用类型 - 传入右值表达式时,T会被推导为 不带引用的原生值类型,最终形参类型
T&&为对应右值引用类型
对应你测试用例的四次调用,T的实际推导结果如下:
log_value(s);:s是A类型的左值,T推导为A&log_value(s1);:s1是const A类型的左值,T推导为const A&log_value(s2);:s2是A&&类型的具名变量,具名变量本身是左值,T推导为A&log_value(A::a);:A::a是A类型的纯右值,T推导为A
而std::is_enum_v的判断逻辑是:仅当传入的类型是不带引用、不带cv修饰的裸枚举类型时返回true,所有引用类型传入都会返回false。这就解释了为什么只有第四次调用(T是裸类型A)输出enum,前三次T都是引用类型,全部返回false输出normal。
const T&版本判断正常的原因
当形参是const T&形式时,模板类型推导会自动剥离传入表达式的引用修饰,仅保留原始值类型,形参需要的const由模板参数外层的const承担,不会进入T的推导结果:
四次测试调用中,T全部被推导为裸类型A,因此std::is_enum_v<T>的判断结果全为true,所有用例都输出enum。
万能引用版本的修复方式
判断原始类型前,先通过类型萃取剥离T上的引用和cv修饰即可,推荐用C++20引入的std::remove_cvref_t,旧版本可以用std::decay_t实现相同效果:
template<typename T> constexpr auto log_value(T&& value) { // 剥离引用、const/volatile修饰后拿到原始类型再判断 if constexpr (std::is_enum_v<std::remove_cvref_t<T>>) { std::cout << "enum" << std::endl; } else { std::cout << "normal" << std::endl; } }
修复后四个测试用例会全部正确输出enum。
内容的提问来源于stack exchange,提问作者f1msch
相关产品推荐
相关产品推荐

