具有引用折叠的函数模板重载

Function template overloading with reference collapsing

除了这个问题,,我还有一个问题。请注意,您不必阅读此 post 即可理解这一点,但这可能会有所帮助。 post 顺便说一句,我认为没有显示出问题的理想解决方案,但这不是重点。

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_bufferoutput_buffer 都是左值引用。第一个实例是第一个有效函数。

它不是模板特化,所以令我惊讶的是,编译器实际上选择了其中一个,而不是给出错误。

copy 的第二个实例可能看起来更具体,但如果 Bufint&,第一个实例也需要两个左值引用。

那么,问题来了,为什么首选二审呢?我希望编译器首先通过替换模板参数实际创建函数。然后,事实证明实例一中的 Buf&& input_buffer 等于 Buf&.

欢迎询问我是否需要详细说明。

模板(1)和(2)都实例化为相同的签名,因此调用哪个取决于偏序规则。部分排序对模板声明进行操作,而不是对隐式实例化产生的特化进行操作,也不对其显式特化进行操作。

根据偏序规则,我们判断(1)是否至少和(2)一样通用,以及(2)是否至少和(1)一样通用。如果答案分别是"yes"和"no",则(2)更专业,因此被选中。

为了确定 (1) 是否至少与 (2) 一样通用,我们为 Buf 合成了一些独特的类型并将其代入 (2) 的签名中,然后尝试推导 (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) 只能生成两个参数都是左值引用的特化。