static_cast不按预期工作优先级

use*_*710 9 c++ templates overloading ambiguous-call c++11

#include <iostream>
#include <cstdint>

template<int T> void foo()
{
  std::cout << "a" << std::endl;
}

template<uint8_t T> void foo()
{
  std::cout << "b" << std::endl;
}

int main()
{
  foo<static_cast<uint8_t>(42)> ();
  foo<static_cast<int>(42)>();
  return(0);
}
Run Code Online (Sandbox Code Playgroud)

知道为什么这不能按预期工作吗?

我的gcc 4.8.1抱怨一个模糊的调用,但static_cast不应该"修复"优先级规则,在这种情况下你有两种类型具有相同的优先级吗?

jog*_*pan 5

您可能会认为编译器在解析重载的函数模板时会尝试更好地确定哪些模板与给定的参数匹配.基于该假设,具有a的模板uint8_t应该匹配函数调用,其uint8_t参数优于模板int.

但这不是模板重载解析的工作原理.模板重载决策(第14.5.6.2节)与普通函数重载决策(第13.3节)不同.它首先建立候选模板然后,而不是试图检查每个匹配给定参数的程度,它只是确定两个(或更多)候选模板中哪一个是最专业的.

请注意,这只是候选模板之间的问题.它没有考虑函数调用的给定参数.(这些被考虑用于类型推导,这只是建立候选模板集的过程的一部分.)

因此,它检查是否uint8_t更专业,int反之亦然(通常 - 不是针对手头函数调用的给定参数).它基本上是通过检查是否uint8_t可以(理论上)使用任何给定的参数来填充int参数而没有非标准转换,反之亦然.情况就是这样(在两个方向上),因此两个模板都不比另一个更专业.因此,模棱两可无法解决.


标准的相关部分如下.

首先,§13.3.3确定当两个函数模板(与两个普通函数,或一个函数和一个模板相对)竞争函数调用时,模板重载机制用于选择最佳:

[...]一个可行的函数F1被定义为比另一个可行函数F2更好的函数如果对于所有参数i,ICSi(F1)不是比ICSi(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而另一个候选参数是非const引用时,它有一些特殊的仲裁规则,以及一些类似的情况;
  • 每个候选模板可以产生多个变换模板,尤其是当有多个模板参数被推导出来时;
  • default-arguments和模板参数包的存在使情况更加复杂.