除了这个问题Function template deduction l-value reference and universal reference,我还有另一个问题。请注意,您不必阅读本文即可了解这一点,但这可能会有所帮助。顺便说一下,这篇文章并没有显示出我所认为的问题的理想解决方案,但这并不是重点。
template <typename Buf>
void copy (
Buf&& input_buffer,
Buf& output_buffer)
{} // 1)
template <typename Buf>
void copy (
Buf& input_buffer,
Buf& output_buffer)
{} // 2)
...
int i = 4;
int j = 6;
copy<int&>(i, j); // copy taking two l-values; calls second instance.
我希望如果我要调用copy<int&>(i,j)
,那么将调用copy
的第一个实例。已指定类型Buf
,因此不需要推导。 Reference collision rules
导致input_buffer
和output_buffer
均为左值引用。第一个实例是第一个有效函数。
这不是模板专门化,因此让编译器实际上选择其中一种而不是给出错误也令我感到惊讶。
copy
的第二个实例可能看起来更具体,但是如果Buf
为int&
,则第一个实例还需要两个l值引用。
所以,问题是,为什么首选第二实例?我希望编译器首先通过替换模板参数来实际创建函数。然后,事实证明实例1中的Buf&& input_buffer
等于Buf&
。
随时问我是否需要详细说明。
答案 0 :(得分:4)
模板(1)和(2)都实例化为相同的签名,因此调用哪个签名取决于部分排序规则。局部排序对模板声明起作用,而不是对隐式实例化产生的特化或显式特化进行操作。
根据偏序规则,我们确定(1)是否至少与(2)一般,以及(2)是否至少与(1)一般。如果答案分别是“是”和“否”,则选择(2)更专业。
要确定(1)是否至少与(2)通用,我们为Buf
合成一个唯一类型并将其替换为(2)的签名,然后尝试推导(1 )从这样产生的假设专业化。这个想法是,如果这适用于唯一类型,那么它也必须适用于所有其他类型。
实际上,如果我们用(2)中的Buf
= Unique
,我们得到
void copy (Unique& input_buffer, Unique& output_buffer)
这导致Buf
在(1)中被推导为Unique&
(这是由于引用折叠规则而起作用的。)
如果相反,将Buf = Unique
替换为(1),则会得到签名
void copy (Unique&& input_buffer, Unique& output_buffer)
现在,我们尝试从此推论(2)中的Buf
---但它当然不起作用,因为(2)只能生成两个参数均为左值引用的特殊化。