4
template<typename T>
struct Test{};

template<typename T>
struct Test<T&&>{};

考虑上面的例子,标准规定类模板部分特化应该比它的主类模板更特化。

在类模板部分特化的参数列表中,适用以下限制:

专业化应比主模板更专业化。

为了确定哪个更专业,将对其应用以下规则:

对于两个类模板部分特化,如果根据函数模板的排序规则,对两个函数模板进行以下重写,则第一个函数模板比​​第二个函数模板更特化:

  • 两个函数模板中的每一个都具有与相应的偏特化相同的模板参数。
  • 每个函数模板都有一个函数形参,其类型是类模板特化,其中模板实参是部分特化的 simple-template-id 的 template-argument-list 中的每个模板实参的函数模板的对应模板形参.

对于主类模板,重写后的函数模板是这样的:

template<typename T>
void ordering(Test<T>)

类模板偏特化的重写函数模板是这样的:

template<typename T>
void ordering(Test<T&&>)

根据“在部分排序期间推导模板参数”的规则:

用于确定排序的类型取决于完成部分排序的上下文:

  • 在函数调用的上下文中,使用的类型是函数调用具有参数的那些函数参数类型。
  • 在调用转换函数的上下文中,使用转换函数模板的返回类型。
  • 在其他上下文中,使用函数模板的函数类型

上面从参数模板中指定的每个类型和参数模板中的相应类型都用作 P 和 A 的类型。如果特定 P 不包含参与模板参数推导的模板参数,则该 P 不用于确定订购。

既不是function call,也不call to a conversion function是这个上下文。所以子弹3有效。这意味着,取void(Test<T>)为 P 和取void(Test<T&&>)为 A,反之亦然。

对于这对 P/A,就是temp.deduct.type#10中提到的情况,即

将 P 的相应参数类型列表 ([dcl.fct]) 的每个参数类型 P i与 A 的相应参数类型列表的相应参数类型 A i进行比较。

模板参数可以在几种不同的上下文中推导出来,但在每种情况下,都会将根据模板参数指定的类型(称为 P)与实际类型(称为 A)进行比较,并尝试查找模板参数值(类型参数的类型,非类型参数的值,或模板参数的模板)将使 P 在替换推导值(称为推导 A)后与 A 兼容。

在这里,我们在每个函数类型中只有一个参数。所以比较一下Test<T>Test<T&&>反之亦然,这个过程在temp.deduct.type#9中提到。

但是我在这里争论的是,标准中没有相关规则说明比较过程的细节是什么。换句话说,为什么我们可以TT&&(T would be T&&) 推导出来,但反过来不行。如果我错过了有关细节的相关规则,请指出。如果标准中确实没有这样的细节描述,那么模板参数推演过程细节的相关技术在哪里可以找到呢?

4

2 回答 2

1

在我看来,您所引用的内容是您正在查看的内容中的一个问题,但它已经被注意到并修复了。特别是,您引用以下内容:

上面从形参模板中指定的每个类型和来自实参模板的相应类型都用作 P 和 A 的类型。如果特定 P 不包含参与模板实参推导的模板参数,则该 P 不用于确定订购。

到 N4800 (也许之前——我还没有确切地追查到何时更改),这已更改为以下内容(§[temp.deduct.partial]/4, 5):

4 上面从参数模板中指定的每个类型和参数模板中的相应类型都用作 P 和 A 的类型。
5 在完成偏序之前,对用于偏序的类型执行某些转换:
(5.1) — 如果 P 是引用类型,则 P 被引用的类型替换。
(5.2) — 如果 A 是引用类型,则将 A 替换为所引用的类型。

这有效地消除了将 T 推导出为引用类型的可能性,因为推导它的类型永远不能是引用。

于 2020-12-13T05:23:03.707 回答
1

在 [temp.deduct.call] 和 [temp.deduct.conv] 中,它提到

通常,推导过程试图找到使推导的 A 与 A 相同的模板参数值。

我相信这个规则是常识,所以在其他子条款中没有提到(在上面的两个子条款中提到,因为这两个子条款中有例外)。

于 2020-12-14T03:50:46.670 回答