我有一个模板函数,func:
template<typename T> void func(T p) { f(p); }
还有一组函数f:
f(SomeType&); f(int); ...
如果我使用引用作为函数参数p实例化模板函数func,而不显式指定模板参数T,那么推导出的类型将不是p的引用类型,而是类型p是对它的引用,例如:
SomeType s; SomeType& ref_to_s = s; func(ref_to_s); // Type deduction results in: func<SomeType>(ref_to_s) func<SomeType&>(ref_to_s); // Need to explicitly specify type to work with reference
所以,我的问题是:
>为什么编译器无法推断出引用类型SomeType&在上述情况?
>有没有办法定义模板函数func,因此类型推导与引用类型一起工作,而没有明确指定模板参数T?
要清楚,我想要两者兼顾(参见上面的函数f):
func(ref_to_s); // Calls func<SomeType&>(ref_to_s) func(1); // Calls func<int>(1)
解决方法
第一部分的答案是从值初始化对象并将引用绑定到值被认为是同样好的(两者都是重载决策中的“完全匹配”),因此不推导出参数的引用.相反,您必须准确指定它.对于相关示例,请考虑假设的结构
T x = f();
其中T是(在这个思想实验期间)你应该推断的东西. x应该是什么 – 一个对象或一个引用?它会取决于什么?当然,右边的表达式有一个值,该值的类型始终是对象类型.你应该如何决定?您可以查询表达式的值类别,但这太微妙了.所以相反,你必须说出你想要的东西:
T x = f(); // x is an object T & r = f(); // r is a reference
在C中,如果将T替换为auto,则实际上可以使用此机制.相同的规则适用于函数模板的模板参数推导.
现在就如何解决你的问题.通常的习惯用法是让函数模板始终采用引用,然后将参数转发给内部调用:
template <typename T> void func(T && t) { f(std::forward<T>(t)); };
现在func的参数始终是任何特化中的引用(但是由于称为“引用折叠”的奇怪规则,它可以是左值或右值引用),并且该值使用相同的值类别组转发到f,并且f的重载分辨率允许在func的原始参数为左值时选择左值引用重载.