【问题标题】:what is the detail of template argument deduction process模板参数推导过程的细节是什么
【发布时间】:2021-01-03 03:08:41
【问题描述】:
template<typename T>
struct Test{};

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

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

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

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

要确定哪个更专业,将对它们应用以下规则:

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

p>
  • 两个函数模板中的每一个都具有与对应的部分特化相同的模板参数。
  • 每个函数模板都有一个函数参数,其类型是类模板特化,其中模板参数是来自函数模板的对应模板参数部分专业化。

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

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

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

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

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

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

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

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

function callcall to a conversion function 都不是这个上下文。所以子弹3有效。也就是说,将void(Test&lt;T&gt;) 视为P,将void(Test&lt;T&amp;&amp;&gt;) 视为A,反之亦然。

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

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

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

这里,我们在每个函数类型中只有一个参数。所以比较Test&lt;T&gt;Test&lt;T&amp;&amp;&gt;,反之亦然,temp.deduct.type#9中提到了这样的过程。

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

【问题讨论】:

    标签: c++ templates language-lawyer partial-ordering


    【解决方案1】:

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

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

    在 N4800 之前(可能是之前——我还没有找到确切的更改时间),已更改为以下内容 (§[temp.deduct.partial]/4, 5):

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

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

    【讨论】:

    • 您能否引用您在此处引用的更改内容的链接。我去看看。
    • @jackX:我做了:[temp.deduct.partial]/4(和 5)。
    • @jackX:这里是N4800,如果您需要帮助(但我会下载副本,而不是在线查看)。
    • 该规则仍然存在于我在问题中给出的标准版本中。在我的问题中,P 是 void(Test) 或 void(Test&lt;T&gt;),该规则并没有说我们可以删除出现在模板参数列表中的引用。这条规则的意思实际上是说如果PTest&lt;T&gt;&amp;&amp;,那么在偏序之前,它应该转换为Test&lt;T&gt;
    【解决方案2】:

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

    一般来说,演绎过程会尝试寻找模板参数 使推导的 A 与 A 相同的值。

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

    【讨论】:

    • 问题是如何找到使推导的A与A相同的模板参数值。标准中没有提到这个细节。
    • @jackX“如何”并不重要。我们只关心结果。即如果存在使推导的 A 与 A 相同的类型,则该类型为推导类型,否则推导失败。
    • @jackX 一个潜在的问题是,如果存在使推导的 A 与 A 相同的多种类型怎么办?但是到目前为止我还没有想出这样的例子。
    • @xskzr 这确实是一个问题。比如,从T*推导出T,我们可以找到T*T,但不是绕道而行,这是为什么呢?这个细节没有写在标准里。
    • @jackX 当我们从A* 推导出P 时,存在一个类型(A*) 使得PA* 相同;当我们从A 推导出P* 时,不存在使P*A 相同的任何类型。
    猜你喜欢
    • 2013-06-05
    • 2012-02-07
    • 1970-01-01
    • 1970-01-01
    • 2015-10-06
    • 2018-12-09
    • 1970-01-01
    • 2020-04-09
    相关资源
    最近更新 更多