C++完美转发困惑:const限定符如何影响函数重载决议?
完美转发中const左值引用重载的匹配问题
示例代码
template<typename X> int mycall(const X& a) { std::cout << ">>> " << "int mycall(const X& a)" << std::endl; return 0; } template<typename X> int mycall(X&& a) { std::cout << ">>> " << "int mycall(X&& a)" << std::endl; return 0; } template <typename T> class CClass { public: CClass(T t) : m_t{t} { } template<typename U> auto operator()(U&& t) { mycall(std::forward<U>(t)); } private: T m_t; }; class A{}; int main() { CClass cc{5}; //1.. int x = 9; cc(x); cc(10); cc(A{}); //4.. A a{}; cc(a); return 0; }
输出结果
>>> int mycall(X&& a) >>> int mycall(X&& a) >>> int mycall(X&& a) >>> int mycall(X&& a)
问题分析
你疑惑的点是:传入左值(第1、4次调用)时,为什么mycall(X&&)被选中,而不是预期的mycall(const X&);但把const X&改成X&后,左值调用就会匹配X&版本。
核心原因:模板重载决议的匹配优先级
原代码中const X&与X&&的匹配逻辑
当调用cc(x)(x是左值int)时:
CClass::operator()的模板参数U会被推导为int&(通用引用规则:传入左值时,模板参数推导为左值引用)。std::forward<U>(t)会将t转发为int&类型的左值。
此时编译器要在两个mycall模板中选择:
- 对于
mycall(const X&):推导X=int,参数类型是const int&。实参int&可以隐式转换为const int&,这是可行匹配,但属于带const资格转换的匹配。 - 对于
mycall(X&&):根据引用折叠规则,当实参是int&时,X会被推导为int&,此时X&& = int& && = int&,参数类型和实参完全一致,属于精确匹配。
在C++重载决议中,精确匹配的优先级高于需要隐式资格转换的匹配,因此mycall(X&&)被选中。
修改为X&后的匹配逻辑
当把mycall(const X&)改成mycall(X&)后:
- 对于左值实参
int&,mycall(X&)推导X=int,参数类型是int&,完全匹配。 mycall(X&&)依然会推导X=int&,参数类型也是int&,同样完全匹配。
这时候编译器会比较两个模板的特化程度:mycall(X&)只能接受左值,属于更特化的模板;而mycall(X&&)是通用引用模板,可以接受左值、右值等所有类型,属于更通用的模板。根据C++模板重载决议规则,更特化的模板会被优先选择,因此此时左值调用会匹配mycall(X&)版本。
const限定符的作用
const限定符在这里的影响是:让const X&版本的匹配需要额外的const资格转换,无法和X&&版本的精确匹配竞争。如果没有const限定,X&版本和X&&版本对左值都是精确匹配,但X&更特化,所以会被选中。
内容的提问来源于stack exchange,提问作者Suat Mutlu
相关产品推荐
相关产品推荐

