我必须将“模板”和“类型名称”关键字放在何处以及为什么?

在模板中,我必须将typenametemplate放在从属名称上的位置和原因是什么?
到底什么是从属名称

我有以下代码:

模板<typename T,typename Tail>//Tail也将是一个UnionNode。
结构UnionNode:公共尾部{
// ...
模板<typename U>结构单元{
//问:在这里哪里添加typename/模板?
typedef Tail::inUnion<U>dummy;
};
模板<gt;结构单元<T&gt{
};
};
模板<typename T>//对于最后一个节点Tn。
结构UnionNode<T、 无效>{
// ...
模板<typename U>结构单元{
char fail[-2+(sizeof(U)%2)];//无法为任何U实例化
};
模板<gt;结构单元<T&gt{
};
};

我遇到的问题是typedef Tail::inUnion<U>虚拟行。我相当肯定,inUnion是一个依赖的名称,VC++完全正确地扼杀了它。
我还知道我应该能够在某个地方添加模板,告诉编译器inUnion是一个模板id。但是具体在哪里呢?然后,它是否应该假设inUnion是一个类模板,即inUnion<U&gt命名类型而不是函数

(关于我的C++11答案,请参见此处)

为了分析C++程序,编译器需要知道某些名称是否为类型。以下示例说明:

t*f;

这应该如何解析?对于许多语言,编译器不需要知道名称的含义就可以解析代码,并且基本上知道一行代码的作用。然而,C++中,根据代码 的含义,可以产生很大不同的解释。如果它是一个类型,那么它将是指针f的声明。然而,如果它不是一个类型,它将是一个乘法。因此,C++标准在第(3/7)段:

有些名称表示类型或模板。通常,每当遇到名称时,在继续解析包含该名称的程序之前,必须确定该名称是否表示这些实体之一。确定这一点的过程称为名称查找

如果t引用模板类型参数,编译器将如何找出名称t::x引用的是什么x可以是可以相乘的静态int数据成员,也可以是可以生成声明的嵌套类或typedef如果名称具有此属性(在知道实际模板参数之前无法查找),则称之为依赖名称(取决于模板参数)。

您可能建议等待用户实例化模板:

让我们等到用户实例化模板,然后再找出t::x*f的真正含义

这将起作用,并且实际上是标准允许的一种可能的实现方法。这些编译器基本上将模板的文本复制到内部缓冲区中,并且仅当需要实例化时,才解析模板并可能检测定义中的错误。但是,其他实现没有因为模板作者的错误而困扰模板的用户(可怜的同事!),而是选择尽早检查模板,并在实例化之前尽快给出定义中的错误

因此,必须有一种方法告诉编译器某些名称是类型,而某些名称不是类型

“typename”关键字

答案是:我们决定编译器应该如何解析它。如果t::x是一个依赖名称,那么我们需要用typename作为前缀,告诉编译器以某种方式解析它。该标准规定为(14.6/2):

模板声明或定义中使用的、依赖于模板参数的名称是
假定不命名类型,除非适用的名称查找找到类型名称或名称是限定的
通过关键字typename

有许多名称不需要typename,因为编译器可以通过模板定义中适用的名称查找,找出如何解析构造本身-例如使用T*f,当T是类型模板参数时。但是对于t::x*f要成为一个声明,它必须写成typename t::x*f。如果省略关键字并且名称被视为非类型,但当实例化发现它表示类型时,编译器会发出通常的错误消息。有时,在定义时给出误差:

//t::x被视为非类型,但作为一个表达式,以下内容忽略了
//运算符或分隔两个名称的分号。
t::xf;

语法只允许在限定名称之前使用typename——因此,如果非限定名称引用类型,则通常认为它们引用类型是理所当然的

正如介绍性文本所暗示的,对于表示模板的名称也存在类似的问题

“模板”关键字

还记得上面的初始引用以及标准如何要求对模板进行特殊处理吗?让我们举一个看起来很无辜的例子:

boost::function<int()>F

这对人类读者来说可能是显而易见的。对于编译器来说并非如此。想象一下以下任意定义的boost::functionf

名称空间boost{int function=0;}
int main(){
int f=0;
boost::函数<int()>f;
}

这实际上是一个有效的表达式!它使用小于运算符将boost::function与零(int())进行比较,然后使用大于运算符将得到的boolf进行比较。然而,正如您可能知道的那样,boost::function在现实生活中是一个模板,因此编译器知道(14.2/3):

在名称查找(3.4)发现某个名称是模板名称后,如果该名称后跟一个<,这个<是
始终作为模板参数列表的开头,决不作为名称后跟小于
接线员

现在我们回到了与typename相同的问题。如果在解析代码时我们还不知道名称是否是模板呢?我们需要按照14.2/4的规定,在模板名称前插入template。这看起来像:

t::模板f<int>(); // 调用函数模板

模板名称不仅可以出现在之后,还可以出现在-&gt或。您还需要在此处插入关键字:

此->模板f<int>(); // 调用函数模板

依赖关系

对于那些书架上有厚厚的标准书的人,如果他们想知道我到底在说什么,我将介绍一下标准中是如何规定的

在模板声明中,根据用于实例化模板的模板参数,某些构造具有不同的含义:表达式可能具有不同的类型或值,变量可能具有不同的类型,或者函数调用可能最终调用不同的函数。这种构造通常被称为依赖于模板参数

该标准通过构造是否依赖来精确定义规则。它将它们分成逻辑上不同的组:一个捕获类型,另一个捕获表达式。表达式可能取决于其值和/或类型。因此,我们附上了一些典型的例子:

  • 依赖类型(例如:类型模板参数T
  • 值相关表达式(例如:非类型模板参数N
  • 类型相关表达式(例如:对类型模板参数的转换(T)0

大多数规则都是直观的,并且是递归建立的:例如,如果N是值依赖表达式或T是依赖类型,则构造为T[N]的类型是依赖类型。有关详细信息,请参见相关类型的(14.6.2/1)、类型相关表达式的(14.6.2.2)、值相关表达式的(14.6.2.3)

从属名称

该标准有点不清楚到底是什么依赖名称。简单阅读一下(你知道,最小惊奇原则),它定义为依赖名的所有内容都是下面函数名的特例。但是由于显然T::x也需要在实例化上下文中查找,因此它也需要是一个依赖名称(幸运的是,从C++14年中期开始,委员会已经开始研究如何修复这个令人困惑的定义)

为了避免这个问题,我对标准文本进行了简单的解释。在所有表示依赖类型或表达式的构造中,有一个子集表示名称。因此,这些名称是“从属名称”。名称可以采用不同的形式-标准规定:

名称是标识符(2.11)、操作员功能id(13.5)、转换功能id(12.3.2)或表示实体或标签(6.6.4、6.1)的模板id(14.2)的使用

标识符只是一个简单的字符/数字序列,而接下来的两个是操作符+操作符类型表单。最后一个表单是模板名称<参数列表&gt。所有这些都是名称,按照标准中的常规用法,名称还可以包括quali

发表评论