通过万能引用无法访问模板类特化的问题排查
关于C++万能引用(Forwarding References)的实例化与特化匹配问题
示例代码
#include <iostream> template <typename T> struct Cls { static void func(T& b) { std::cout << 1 << std::endl; } }; template <> struct Cls<int> { static void func(int& b) { std::cout << 2 << std::endl; } }; template <typename T> void some_func(T&& a) { Cls<T>::func(a); } int main() { int i{3}; some_func(i); some_func(3); }
我的理解
万能引用可以匹配左值或右值:
- (A) 调用
some_func(i);时,i是int类型左值,a的类型应为int&,T应为int,应匹配特化的Cls<int>::func(int&),输出应为2。 - (B) 调用
some_func(3);时,a的类型应为int&&,不确定输出结果。
实际输出
1 2
注:非原生类型T也存在相同行为。
问题
- 编译器是否会根据传入的引用类型实例化不同版本的
some_func()?即分别实例化some_func(int& a)和some_func(int&& a),还是同一实例化函数内的特殊引用类型? - 为何第一个输出是1?为何未使用特化版本?我的(A)理解是否错误?T应为int,为何反而选择通用实现?
- 为何第二个输出是2?第一个调用类型匹配时未匹配特化,为何第二个类型不匹配时反而匹配?
- 这是否与a在两种情况下都是xvalue有关?即rvalue因存储在a中被解释为lvalue?这能解释第二个输出,但无法解释第一个?
解答
问题1
编译器会实例化两个完全不同的some_func版本,核心是引用折叠规则:
- 传入左值
int i时,模板参数T被推导为int&,T&&折叠为int&,最终实例化出void some_func(int& a)。 - 传入右值
3时,模板参数T被推导为int,T&&就是int&&,实例化出void some_func(int&& a)。
问题2
你的(A)理解错误,问题出在万能引用的模板参数推导逻辑:
调用some_func(i)时,i是左值,根据C++标准对万能引用的推导规则,T会被推导为int&而非int。此时Cls<T>等价于Cls<int&>,只能匹配通用模板Cls<T>,而不是你写的特化版本Cls<int>,因此调用的是通用模板里的func(T& b)(引用折叠后是int&),输出1。
问题3
调用some_func(3)时,3是右值,T被推导为int,所以Cls<T>就是特化的Cls<int>,自然调用特化版本的func(int& b)。这里需要注意:虽然a的类型是int&&,但变量名作为表达式时是左值,完全可以绑定到int&类型的参数上,所以最终输出2。
问题4
和xvalue无关,核心是两次调用的模板参数T推导结果不同:
- 第一个调用中
T是int&,匹配通用模板; - 第二个调用中
T是int,匹配特化模板。变量a作为左值表达式的属性,只影响func的参数绑定,不影响T的推导和Cls模板的选择。
内容的提问来源于stack exchange,提问作者SporreKing
相关产品推荐
相关产品推荐

