static_cast不按预期工作优先级

时间:2013-06-26 23:50:37

标签: c++ templates c++11 overloading ambiguous-call

#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);
}

任何想法为什么这不能按预期工作?

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

1 个答案:

答案 0 :(得分:5)

您会认为编译器在解析重载的函数模板时,会尝试更好地确定哪些模板与给定的参数匹配。基于该假设,具有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模板),即{{1}可以在没有非标准转换的情况下推导出其他模板的参数。答案是肯定的,它可以。

然后走另一条路,拿走int模板,合成一个值,然后尝试“匹配”int模板,即如果uint8_t参数可以推导出来而不是非 - 标准转换。答案是肯定的。

如果仅在一个方向上起作用,则两个模板中的一个必须比另一个模板更专业,并且选择它们来解决模糊性。如果它两种方式都有效(如你的情况那样),那么歧义就无法解决了。

注意。整个程序实际上比较复杂,而且它在标准中的描述很长,主要原因如下:

  • 类型演绎过程本身很复杂。它确实允许某些隐式转换(基本上,标准转换包括一些与cv限定符相关的转换);
  • 当一个候选参数是const-reference而另一个候选参数是非const引用时,它有一些特殊的仲裁规则,以及一些类似的情况;
  • 每个候选模板可能会产生多个转换后的模板,尤其是当要推导出多个模板参数时;
  • 默认参数和模板参数包的存在使情况更加复杂。