我有一个模板函数func
:
template<typename T>
void func(T p) { f(p); }
一组函数f
:
f(SomeType&);
f(int);
...
如果我使用引用作为函数参数func
实例化模板函数p
,而没有明确指定模板参数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)
答案 0 :(得分:9)
第一部分的答案是,从一个值初始化一个对象并将一个引用绑定到一个值被认为是同样好的(两者都是&#34;完全匹配&#34;在重载决策中),因此引用的参考不推断出参数。相反,您必须准确指定它。对于相关示例,请考虑假设构造
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
的参数始终是任何专业化的参考(但是由于一个名为&#34的奇怪规则;参考折叠&#34;它可以是左值或左值参考) ,并且该值被转发,具有相同的值类别组到f
,并且f
上的此重载解析允许在{的原始参数时选择左值引用重载。 {1}}是一个左值。
答案 1 :(得分:7)
使用forwarding-reference:
template<typename T>
void func(T && p) //notice &&
{
f(std::forward<T>(p)); //notice std::forward
}
现在请搜索此站点上的转发引用(或通用引用,如前所述)以及std::forward
以了解有关它们的更多信息。
答案 2 :(得分:0)
考虑以下示例:
template<typename T>
void f(T value, T& ref) {// fake swapping
T temp = ref;
ref = value;
value = temp;
}
当编译器试图推导出T的类型时,合理的做法是首先从“value”和“ref”中删除可能的引用,以便
int i = 1;
int j = 2;
f(i, j); //value of j becomes 1.
//up to here, value of i is still 1
否则,事情会变得非常奇怪。如果T被推断为T&amp;,则f(i,j)的类型将等于f(int&amp;,int&amp;&amp;)。假设T&amp; &安培;折叠到T&amp;然后它变成f(int&amp;,int&amp;)。这完全违背了程序员的意图。