从呼叫的类型推断的上下文中“推断的A”的含义

时间:2019-04-07 15:51:04

标签: c++ language-lawyer

  

如果P是一个类,并且P的形式为simple-template-id,则转换后的A可以是推导的{{ 1}}。

from [temp.deduct.call]4.3

这句话描述了如果函数模板参数是从“推导的D派生出来的,那么它如何仍然有效,但是,对于“推导的A”实际上是什么没有一个明确的定义。

我的理论是,推论出的A是原始的A,其中替换了A中的模板参数,但这会破坏试图找到使{{ 1}}并推导P相同,因为在某些情况下,A是非参考,而推导A是参考。

1 个答案:

答案 0 :(得分:3)

推导函数模板参数的目的是弄清楚在使用模板名称(如函数名称)的地方应使用函数模板的哪个特殊化。例如,给定功能模板

template <typename T>
void f(T* value) {}

当您有类似的函数调用时

int* a = &x;
f(a);

这里的名称f实际上不是函数的名称,而是函数模板的名称。编译器必须根据函数调用中给定的参数类型,来确定该调用实际上应该调用哪个具体的函数模板。换句话说,必须弄清楚应该为模板参数X使用哪个模板参数T才能到达可以在此处调用的实际函数f<X>。与普通函数调用相比,这是一个反问题。现在不必使参数列表适合给定的签名(通过应用转换),我们现在必须使签名适合给定的参数列表。另一种看待它的方法是尝试推导模板参数,这些参数会使每个函数参数的类型与每个函数调用参数的类型匹配。这是[temp.deduct.call]/4在这里谈论的内容:

  

通常,推导过程会尝试查找模板参数值,以使推导的A与A相同

以上面的示例为例,给定一些推导模板参数X,推导参数类型就是通过将推导X替换为T到函数参数类型{{1 }}(即此函数参数采用的参数类型)。如果我们推论T*X,则将int的{​​{1}}替换为int会使我们推论的参数类型为T。由于推导的参数类型T*与实际参数的类型相同,因此我们发现函数int*是我们想要的。

要使所有这些与正常函数调用的行为保持一致,请注意一些特殊情况。特别是对于数组和函数类型的函数调用参数,通常我们具有数组到指针和函数到指针的衰减以及顶级const。为了解决这个问题,该标准指定我们尝试匹配的参数类型int*不仅被简单地视为相应函数调用参数的类型,而是首先被 transformed 应用数组到指针,函数到指针等的转换。经过转换的 f<int>是我们实际上试图使推论的参数类型匹配的A。这仅仅是为了解释为什么标准在此谈论“已转换的A”。对于手头的问题,它并不是那么重要。转换后的A只是我们实际上试图匹配的函数参数类型。

现在,我们有一些

A

和一些派生类

A

当您拥有类似的功能模板时

template <typename T> class B {};

和这样的函数调用

class D : public B<int> {};

没有模板参数template <typename T> void f(const B<T>*) {} ,您可以选择D d; f(&d); ,以使推导的参数类型X等于T。但是,由于const B<X>*是从D*派生的,因此将模板参数推论为D仍然会导致函数专业化B<int>可以接受该调用。 [temp.deduct.call]/4.3整个段落,尤其是您问题中的句子

  

如果int是一个类,并且f<int>的形式是 simple-template-id ,则转换后的P可以是派生类{{1 }}中的推论P

有没有让它确切地起作用……