你会认为编译器在解析重载的函数模板时会尝试找出哪个模板更好地匹配给定的参数。基于该假设,带有 a 的模板应该比带有 的模板更好地uint8_t
匹配带有参数的函数调用。uint8_t
int
但这不是模板重载解析的工作方式。模板重载决议(§14.5.6.2)不同于普通函数重载决议(§13.3)。它首先建立候选模板,然后,而不是尝试检查每个模板与给定参数的匹配程度,它只是确定两个(或更多)候选模板中的哪一个是最专业的。
请注意,这只是候选模板之间的问题。它不考虑函数调用的给定参数。(这些被考虑到类型推导,这只是建立候选模板集的过程的一部分。)
因此,它检查是否uint8_t
比更专业,int
反之亦然(通常——不是针对手头函数调用的给定参数)。它基本上是通过检查任何给定的uint8_t
参数是否可以(理论上)在没有非标准转换的情况下用于填充int
参数来做到这一点,反之亦然。情况就是这样(在两个方向上),所以没有一个模板比另一个更专业。因此无法解决歧义。
本标准的相关章节如下。
首先,第 13.3.3 节规定,当两个函数模板(相对于两个普通函数,或者一个函数和一个模板)竞争一个函数调用时,使用模板重载机制来选择最好的:
[...] 如果对于所有参数 i,ICSi(F1) 不是比 ICSi(F2) 更差的转换序列,则将可行函数 F1 定义为比另一个可行函数 F2 更好的函数,然后
[...] — F1 和 F2 是函数模板特化,根据 14.5.6.2 中描述的部分排序规则,F1 的函数模板比 F2 的模板更特化。
然后,§14.5.6.2 很长,但最相关的部分是:
(2) 部分排序通过依次转换每个模板(参见下一段)并使用函数类型执行模板参数推导来选择两个函数模板中的哪一个比另一个更专业。推演过程确定模板中的一个是否比另一个更专业。如果是这样,更专业的模板是部分排序过程选择的模板。
(3) 为了生成转换后的模板,对于每个类型、非类型或模板模板参数(包括其模板参数包(14.5.3)),分别合成一个唯一的类型、值或类模板,并将其替换为每次出现模板的函数类型中的该参数。[...]
(4) 使用转换后的函数模板的函数类型,对另一个模板执行类型推导,如 14.8.2.4 所述。
所以这里的想法是:获取uint8_t
模板并通过将参数替换为实际的合成值来转换它uint8_t
(我猜该值可以从实际的函数调用中获取,但标准并没有这么说)。然后使用类型推导过程检查转换后的模板,作为函数调用,是否会“匹配”另一个模板(即int
模板),即是否int
可以在没有非标准转换的情况下推导出另一个模板的参数。答案是肯定的,可以。
然后走另一条路,取int
模板,合成一个值并尝试它是否“匹配”uint8_t
模板,即是否uint8_t
可以在没有非标准转换的情况下推导出参数。答案是肯定的。
如果这仅在一个方向上起作用,则两个模板中的一个必须比另一个更专业,并且被选择来解决歧义。如果它可以双向工作(如您的情况),则无法解决歧义。
笔记。整个过程其实比较复杂,标准中的描述也很长,主要有以下几个原因:
- 类型推断过程本身很复杂。它确实允许某些隐式转换(基本上是标准转换,包括一些与 cv 限定符相关的);
- 对于一个候选参数是 const-reference 而另一个是 non-const 引用的情况,以及一些类似的情况,它有许多特殊的仲裁规则;
- 每个候选模板可能会产生多个转换模板,尤其是当有多个模板参数需要推导时;
- 默认参数和模板参数包的存在使情况更加复杂。