重载解析和部分模板排序

Overload resolution and partial template ordering

考虑一下这对简单的函数模板。

template <typename T>
void foo(T& ) { std::cout << __PRETTY_FUNCTION__ << '\n'; }

template <typename C>
void foo(const C& ) { std::cout << __PRETTY_FUNCTION__ << '\n'; }

如果我们用非常量参数调用foo

int i = 4;
foo(i);

基于[over.ics.rank]/3.2.6,首选T&重载,因为推导的引用int&较少cv - 比推导的引用合格 const int&.

但是,如果我们用常量参数调用 foo

const int ci = 42;
foo(ci);

首选 const C& 重载,因为它 "more specialized" 基于 [over.match.best]/1.7。但是确定这一点的规则是什么?我的理解是,您为 C 合成了一个类型(称其为 M)并尝试对 foo(M) 执行推导 - 但那会成功(使用 T == M)。只有右值会导致推导失败 - 但编译器如何知道它必须在综合步骤中选择右值?

免责声明:我们考虑的类型始终是参数类型。 types/value categories/etc。传递的实际参数仅在重载决策中考虑,从不在偏序中考虑。

部分排序在两个 "turns" 中考虑两个重载,其中一个模板始终是 参数模板 ,另一个模板是 参数模板。 [temp.deduct.partial]/2:

For each of the templates involved there is the original function type and the transformed function type. [..] The deduction process uses the transformed type as the argument template and the original type of the other template as the parameter template. This process is done twice for each type involved in the partial ordering comparison: once using the transformed template-1 as the argument template and template-2 as the parameter template and again using the transformed template-2 as the argument template and template-1 as the parameter template.

您应该熟悉转换 "templates" 的生成方式。这在 §14.5.6.2/3 中指定。

To produce the transformed template, for each type, non-type, or template template parameter (including template parameter packs (14.5.3) thereof) synthesize a unique type, value, or class template respectively and substitute it for each occurrence of that parameter in the function type of the template.

所以我们的(转换后的)参数模板是

void foo( Unique1& );

void foo( Unique2 const& );

[temp.deduct.partial]/3 & /4:

The types used to determine the ordering depend on the context in which the partial ordering is done:

  • In the context of a function call, the types used are those function parameter types for which the function call has arguments. [..]

Each type nominated above from the parameter template and the corresponding type from the argument template are used as the types of P and A.

因此我们有两个回合,在两个回合中我们都有一个类型 P 和一个类型 A:

转弯 1:
P1T const&
A1Unique1&

第 2 回合:
P2T&
A2Unique2 const&

但在乐趣开始之前,还要对这些类型执行一些转换 - 我缩写为 [temp.deduct.partial]/5 & /7:

  • 如果 PA 是引用,则它们将替换为它们引用的类型。
  • 删除所有顶级 cv 限定符。

请注意,删除的 cv 限定符是 "remembered" 以备后用。 [temp.deduct.partial]/6:

If both P and A were reference types (before being replaced with the type referred to above), determine which of the two types (if any) is more cv-qualified than the other; otherwise the types are considered to be equally cv-qualified for partial ordering purposes. The result of this determination will be used below.

因此我们剩下

转弯 1:
P1T
A1Unique1

第 2 回合:
P2T
A2Unique2

现在我们执行推导 - 通过设置 T=Unique[1/2],这显然在两个回合都成功了。来自 [temp.deduct.partial]/8:

If deduction succeeds for a given type, the type from the argument template is considered to be at least as specialized as the type from the parameter template.

这让我们知道 Unique1& 至少和 T const& 一样专业,Unique2 const& 至少和 T& 一样专业。


但是,这是 [temp.deduct.partial]/(9.2) 介入的地方:

If, for a given type, deduction succeeds in both directions (i.e., the types are identical after the transformations above) and both P and A were reference types (before being replaced with the type referred to above):

  • [..]; otherwise,

  • if the type from the argument template is more cv-qualified than the type from the parameter template (as described above), the parameter type is not considered to be at least as specialized as the argument type.

记住的简历限定符开始发挥作用。 A2"more cv-qualified (as described above)" 而不是 P2,因此 P2 不被认为至少与 A2.

最后,[temp.deduct.partial]/10:

Function template F is at least as specialized as function template G if, for each pair of types used to determine the ordering, the type from F is at least as specialized as the type from G.
F is more specialized than G if F is at least as specialized as G and G is not at least as specialized as F.

意味着由于类型 T& 至少不像 Unique2 const& 那样专门化 我们已经确定 T const& 至少是与 Unique1& 一样专业,T const&-重载比 T&-重载更专业。


第 9 段中的上述规则目前是 CWG #2088 四个月前由 R. Smith 创建的主题:

The late tiebreakers for lvalue-vs-rvalue references and cv-qualification in 14.8.2.4 [temp.deduct.partial] paragraph 9 are applied

If, for a given type, deduction succeeds in both directions (i.e., the types are identical after the transformations above) and both P and A were reference types (before being replaced with the type referred to above):

但是,这是基于错误的假设。 [..] 我们需要确定规则是“双向推导成功”还是“类型相同”。后者似乎更合理。

虽然这不会改变建立的结果,因为我们得到的类型确实是相同的。