c++ - 重载解析和部分模板排序

标签 c++ templates language-lawyer overload-resolution

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

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& 限定。

但是,如果我们使用 const 参数调用 foo:

const int ci = 42;
foo(ci);

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

最佳答案

免责声明:我们考虑的类型始终是参数类型。类型/值类别/等等。所传递的实际参数仅在重载决策中考虑,从不按部分顺序考虑。

部分排序考虑两个“回合”中的重载,其中一个模板始终是参数模板,另一个模板是参数模板。 [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.

您应该熟悉转换后的"template"的生成方式。这在第 §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 回合:
P1 :   T const&
A1 :   Unique1&

第二回合:
P2 :   T&
A2 :   Unique2 const&

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

  • 如果PA是引用,然后它们被它们引用的类型替换。
  • 所有顶级简历限定符都会被删除。

请注意,删除的 cv 限定符将被“记住”以供稍后使用。 [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 回合:
P1 :   T
A1 :   Unique1

第二回合:
P2 :   T
A2 :   Unique2

现在我们执行演绎 - 通过设置 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.

记住的简历限定符开始发挥作用。 A2P2“更符合简历要求(如上所述)” ,因此 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& -overload 比 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):

然而,这是基于错误的假设。 [..] 我们需要决定规则是“双向推演成功”还是“类型相同”。看来后者更合理。

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

关于c++ - 重载解析和部分模板排序,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31051189/

相关文章:

c++ - 重复的 typedef - 在 C 中无效但在 C++ 中有效?

c++ - 模板特化中的隐式转换

c++ - OpenSSL 解密失败或坏记录 mac boost::asio

c++ - 为什么运行时环境不能决定应用 delete 或 delete[] 而不是程序员?

c++ - gtkmm:窗口内的模态小部件

c++ - 一个类如何继承自己?

c++ - 为什么 Visual C++ 不能部分特化模板中的类?

c++ - 将 C++ 转换为模板元程序

c++ - 继承的构造函数,在 clang++3.9 中编译,在 g++7 中失败

c++ - 将鼠标单击事件添加到使用 QtCreator 创建的 QGraphicsView