C++模板编程:左值传参触发SFINAE失败、右值传参编译通过原因
问题说明
检索资料未找到明确对应解释:为何仅当参数以左值引用形式传递时会触发SFINAE,参数以右值引用形式传递时却可编译构建成功?复现代码如下:
template <typename T> class A { public: using member_type = T; }; template <typename AType> typename AType::member_type f(AType&& m) { typename AType::member_type res{}; return res; } void demo() { A<int> a; // ERROR: candidate template ignored: substitution failure // [with AType = A<int> &]: type 'A<int> &' // cannot be used prior to '::' because it has no members f(a); // BUILD SUCCESS f(std::move(a)); }
核心原因
问题本质是转发引用(万能引用)的模板类型推导规则和引用类型无法访问嵌套成员两个特性共同导致的:
- 首先明确:函数形参
AType&& m不是普通右值引用,是转发引用——只有当&&直接修饰函数模板的独立类型形参时,才会触发转发引用的特殊推导规则。 - 转发引用的推导逻辑:
- 传入左值实参时(即示例中
f(a),命名变量a是左值),模板形参AType会被推导为左值引用类型,本场景下推导结果为A<int>& - 传入右值实参时(即示例中
f(std::move(a)),std::move(a)是将亡值、属于右值类别),模板形参AType会被推导为原生值类型,本场景下推导结果为A<int>
- 传入左值实参时(即示例中
- 报错的直接触发点:C++标准规定,引用类型本身不持有任何成员(包括嵌套类型定义、成员函数、成员变量),只有被引用的原类型才有成员访问权限。
- 左值传参时,
AType被推导为A<int>&,此时写AType::member_type等价于尝试访问A<int>&这个引用类型的嵌套类型,替换阶段直接失败,触发SFINAE,该模板候选被排除,最终因无匹配函数报错 - 右值传参时,
AType被推导为A<int>,该类明确定义了嵌套类型member_type,替换过程无错误,因此可以正常编译通过
- 左值传参时,
修复方案
如果需要同时支持左值、右值传参,只需要在访问嵌套类型前,通过类型萃取去掉模板形参的引用修饰即可:
#include <type_traits> template <typename AType> // C++14及以上可使用std::remove_reference_t简化写法 typename std::remove_reference_t<AType>::member_type f(AType&& m) { typename std::remove_reference_t<AType>::member_type res{}; return res; }
修改后无论传入左值还是右值,都会先剥离AType上可能存在的引用修饰,得到原类型A<int>后再访问嵌套类型,不会触发替换失败。
内容的提问来源于stack exchange,提问作者llxxee
相关产品推荐
相关产品推荐

