我必须在哪里以及为什么要放置“模板"?和“类型名称"关键词? [英] Where and why do I have to put the "template" and "typename" keywords?

查看:39
本文介绍了我必须在哪里以及为什么要放置“模板"?和“类型名称"关键词?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在模板中,我必须在何处以及为什么必须将 typenametemplate 放在依赖名称上?
究竟什么是从属名称?

我有以下代码:

模板//Tail 也将是一个 UnionNode.结构联合节点:公共尾巴{//...模板<类型名 U>结构联合{//Q: 在哪里添加类型名/模板?typedef Tail::inUnion假;};模板<>struct inUnion<T>{};};模板<typename T>//对于最后一个节点 Tn.struct UnionNode<T, void>{//...模板<类型名 U>结构联合{字符失败[-2 + (sizeof(U)%2)];//不能为任何 U 实例化};模板<>struct inUnion<T>{};};

我遇到的问题在于 typedef Tail::inUnion<U>虚拟行.我相当肯定 inUnion 是一个从属名称,VC++ 对此感到窒息是完全正确的.
我也知道我应该能够在某处添加 template 来告诉编译器 inUnion 是一个模板 ID.但具体在哪里?那么它是否应该假设 inUnion 是一个类模板,即 inUnion 命名一个类型而不是一个函数?

解决方案

(请参阅 这里也有我的 C++11 答案

a>)

为了解析 C++ 程序,编译器需要知道某些名称是否是类型.下面的例子证明:

t * f;

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

<块引用>

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

如果 t 指的是模板类型参数,编译器将如何找出名称 t::x 指的是什么?x 可以是可以相乘的静态 int 数据成员,或者同样可以是可以屈服于声明的嵌套类或 typedef.如果一个名称具有这个属性——在知道实际模板参数之前无法查找它——那么它被称为一个依赖名称(它依赖于"模板参数).

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

<块引用>

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

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

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

类型名"关键字

答案是:我们决定编译器应该如何解析它.如果 t::x 是一个依赖名称,那么我们需要在它前面加上 typename 来告诉编译器以某种方式解析它.该标准在 (14.6/2) 上说:

<块引用>

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

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

//t::x 被视为非类型,但作为表达式,下面的//两个名称之间的运算符或分隔它们的分号.t::x f;

语法允许 typename 仅在限定名称之前 - 因此,如果非限定名称这样做,则总是知道它们引用类型是理所当然的.

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

模板"关键字

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

boost::function<整数()>F;

对于人类读者来说,它可能看起来很明显.对于编译器来说不是这样.想象一下 boost::functionf 的以下任意定义:

命名空间提升 { int function = 0;}int main() {整数 f = 0;升压::函数<整数()>F;}

这实际上是一个有效的表达式!它使用小于运算符将 boost::function 与零 (int()) 进行比较,然后使用大于运算符比较生成的 boolf.但是您可能很清楚,boost::function 在现实生活中是一个模板,所以编译器知道(14.2/3):

<块引用>

在名称查找(3.4)后发现一个名称是一个模板名称,如果这个名称后跟一个<,<是总是作为模板参数列表的开头,从不作为名称后跟小于号运算符.

现在我们回到与 typename 相同的问题.如果我们在解析代码时还不知道名称是否是模板怎么办?我们需要在 14.2/4 指定的模板名称之前插入 template.这看起来像:

t::template f();//调用函数模板

模板名称不仅可以出现在 :: 之后,还可以出现在类成员访问中的 ->. 之后.您还需要在此处插入关键字:

this->template f();//调用函数模板

<小时>

依赖关系

对于那些书架上有厚厚的标准书并且想知道我到底在说什么的人,我会谈谈标准中是如何规定的.

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

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

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

大多数规则都是直观的,并且是递归构建的:例如,构造为 T[N] 的类型是依赖类型,如果 N 是值-依赖表达式或 T 是依赖类型.这方面的细节可以在 (14.6.2/1) 部分阅读依赖类型,(14.6.2.2) 用于类型依赖表达式和 (14.6.2.3) 用于与值相关的表达式.

从属名称

标准有点不清楚究竟是什么依赖名称.在简单的阅读中(你知道,最不意外的原则),它定义为 从属名称 是下面函数名称的特殊情况.但是由于显然 T::x 也需要在实例化上下文中查找,它也需要是一个依赖名称(幸运的是,从 C++14 中期开始,委员会已经开始研究如何解决这个令人困惑的定义).

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

<块引用>

名称是标识符 (2.11)、operator-function-id (13.5)、conversion-function-id (12.3.2) 或 template-id (14.2) 的使用,表示实体或标签 (6.6.4, 6.1)

标识符只是一个简单的字符/数字序列,而接下来的两个是 operator +operator type 形式.最后一种形式是 template-name .所有这些都是名称,按照标准中的常规用法,名称还可以包含限定符,说明应该在哪个命名空间或类中查找名称.

值依赖表达式 1 + N 不是名称,但 N 是.作为名称的所有依赖结构的子集称为依赖名称.然而,函数名称在模板的不同实例中可能具有不同的含义,但不幸的是,它并没有被这个一般规则所捕获.

依赖函数名

本文主要关注的不是,但仍然值得一提:函数名称是一个单独处理的异常.标识符函数名称不依赖于它本身,而是依赖于调用中使用的类型依赖的参数表达式.在示例 f((T)0) 中,f 是从属名称.在标准中,这是在 (14.6.2/1) 中指定的.

附加说明和示例

在足够多的情况下,我们同时需要 typenametemplate.您的代码应如下所示

模板结构联合节点:公共尾巴{//...模板<类型名 U>结构联合{typedef typename Tail::template inUnion;假;};//...};

关键字template 不必总是出现在名称的最后部分.它可以出现在用作作用域的类名之前的中间,如下例所示

typename t::template iterator::value_type v;

在某些情况下,关键字是被禁止的,如下所述

  • 在依赖基类的名字上你不能写typename.假定给定的名称是类类型名称.对于基类列表和构造函数初始化列表中的名称都是如此:

     模板 <typename T>struct derived_from_Has_type :/* typename */SomeBase<T>::type{ };

  • 在 using-declarations 中,不能在最后一个 :: 之后使用 template,并且 C++ 委员会 不解决解决方案.

     模板 <typename T>struct derived_from_Has_type : SomeBase<T>{使用 SomeBase<T>::template 类型;//错误使用类型名 SomeBase::type;//类型名 *is* 允许};

In templates, where and why do I have to put typename and template on dependent names?
What exactly are dependent names anyway?

I have the following code:

template <typename T, typename Tail> // Tail will be a UnionNode too.
struct UnionNode : public Tail {
    // ...
    template<typename U> struct inUnion {
        // Q: where to add typename/template here?
        typedef Tail::inUnion<U> dummy; 
    };
    template< > struct inUnion<T> {
    };
};
template <typename T> // For the last node Tn.
struct UnionNode<T, void> {
    // ...
    template<typename U> struct inUnion {
        char fail[ -2 + (sizeof(U)%2) ]; // Cannot be instantiated for any U
    };
    template< > struct inUnion<T> {
    };
};

The problem I have is in the typedef Tail::inUnion<U> dummy line. I'm fairly certain that inUnion is a dependent name, and VC++ is quite right in choking on it.
I also know that I should be able to add template somewhere to tell the compiler that inUnion is a template-id. But where exactly? And should it then assume that inUnion is a class template, i.e. inUnion<U> names a type and not a function?

解决方案

(See here also for my C++11 answer)

In order to parse a C++ program, the compiler needs to know whether certain names are types or not. The following example demonstrates that:

t * f;

How should this be parsed? For many languages a compiler doesn't need to know the meaning of a name in order to parse and basically know what action a line of code does. In C++, the above however can yield vastly different interpretations depending on what t means. If it's a type, then it will be a declaration of a pointer f. However if it's not a type, it will be a multiplication. So the C++ Standard says at paragraph (3/7):

Some names denote types or templates. In general, whenever a name is encountered it is necessary to determine whether that name denotes one of these entities before continuing to parse the program that contains it. The process that determines this is called name lookup.

How will the compiler find out what a name t::x refers to, if t refers to a template type parameter? x could be a static int data member that could be multiplied or could equally well be a nested class or typedef that could yield to a declaration. If a name has this property - that it can't be looked up until the actual template arguments are known - then it's called a dependent name (it "depends" on the template parameters).

You might recommend to just wait till the user instantiates the template:

Let's wait until the user instantiates the template, and then later find out the real meaning of t::x * f;.

This will work and actually is allowed by the Standard as a possible implementation approach. These compilers basically copy the template's text into an internal buffer, and only when an instantiation is needed, they parse the template and possibly detect errors in the definition. But instead of bothering the template's users (poor colleagues!) with errors made by a template's author, other implementations choose to check templates early on and give errors in the definition as soon as possible, before an instantiation even takes place.

So there has to be a way to tell the compiler that certain names are types and that certain names aren't.

The "typename" keyword

The answer is: We decide how the compiler should parse this. If t::x is a dependent name, then we need to prefix it by typename to tell the compiler to parse it in a certain way. The Standard says at (14.6/2):

A name used in a template declaration or definition and that is dependent on a template-parameter is assumed not to name a type unless the applicable name lookup finds a type name or the name is qualified by the keyword typename.

There are many names for which typename is not necessary, because the compiler can, with the applicable name lookup in the template definition, figure out how to parse a construct itself - for example with T *f;, when T is a type template parameter. But for t::x * f; to be a declaration, it must be written as typename t::x *f;. If you omit the keyword and the name is taken to be a non-type, but when instantiation finds it denotes a type, the usual error messages are emitted by the compiler. Sometimes, the error consequently is given at definition time:

// t::x is taken as non-type, but as an expression the following misses an
// operator between the two names or a semicolon separating them.
t::x f;

The syntax allows typename only before qualified names - it is therefor taken as granted that unqualified names are always known to refer to types if they do so.

A similar gotcha exists for names that denote templates, as hinted at by the introductory text.

The "template" keyword

Remember the initial quote above and how the Standard requires special handling for templates as well? Let's take the following innocent-looking example:

boost::function< int() > f;

It might look obvious to a human reader. Not so for the compiler. Imagine the following arbitrary definition of boost::function and f:

namespace boost { int function = 0; }
int main() { 
  int f = 0;
  boost::function< int() > f; 
}

That's actually a valid expression! It uses the less-than operator to compare boost::function against zero (int()), and then uses the greater-than operator to compare the resulting bool against f. However as you might well know, boost::function in real life is a template, so the compiler knows (14.2/3):

After name lookup (3.4) finds that a name is a template-name, if this name is followed by a <, the < is always taken as the beginning of a template-argument-list and never as a name followed by the less-than operator.

Now we are back to the same problem as with typename. What if we can't know yet whether the name is a template when parsing the code? We will need to insert template immediately before the template name, as specified by 14.2/4. This looks like:

t::template f<int>(); // call a function template

Template names can not only occur after a :: but also after a -> or . in a class member access. You need to insert the keyword there too:

this->template f<int>(); // call a function template


Dependencies

For the people that have thick Standardese books on their shelf and that want to know what exactly I was talking about, I'll talk a bit about how this is specified in the Standard.

In template declarations some constructs have different meanings depending on what template arguments you use to instantiate the template: Expressions may have different types or values, variables may have different types or function calls might end up calling different functions. Such constructs are generally said to depend on template parameters.

The Standard defines precisely the rules by whether a construct is dependent or not. It separates them into logically different groups: One catches types, another catches expressions. Expressions may depend by their value and/or their type. So we have, with typical examples appended:

  • Dependent types (e.g: a type template parameter T)
  • Value-dependent expressions (e.g: a non-type template parameter N)
  • Type-dependent expressions (e.g: a cast to a type template parameter (T)0)

Most of the rules are intuitive and are built up recursively: For example, a type constructed as T[N] is a dependent type if N is a value-dependent expression or T is a dependent type. The details of this can be read in section (14.6.2/1) for dependent types, (14.6.2.2) for type-dependent expressions and (14.6.2.3) for value-dependent expressions.

Dependent names

The Standard is a bit unclear about what exactly is a dependent name. On a simple read (you know, the principle of least surprise), all it defines as a dependent name is the special case for function names below. But since clearly T::x also needs to be looked up in the instantiation context, it also needs to be a dependent name (fortunately, as of mid C++14 the committee has started to look into how to fix this confusing definition).

To avoid this problem, I have resorted to a simple interpretation of the Standard text. Of all the constructs that denote dependent types or expressions, a subset of them represent names. Those names are therefore "dependent names". A name can take different forms - the Standard says:

A name is a use of an identifier (2.11), operator-function-id (13.5), conversion-function-id (12.3.2), or template-id (14.2) that denotes an entity or label (6.6.4, 6.1)

An identifier is just a plain sequence of characters / digits, while the next two are the operator + and operator type form. The last form is template-name <argument list>. All these are names, and by conventional use in the Standard, a name can also include qualifiers that say what namespace or class a name should be looked up in.

A value dependent expression 1 + N is not a name, but N is. The subset of all dependent constructs that are names is called dependent name. Function names, however, may have different meaning in different instantiations of a template, but unfortunately are not caught by this general rule.

Dependent function names

Not primarily a concern of this article, but still worth mentioning: Function names are an exception that are handled separately. An identifier function name is dependent not by itself, but by the type dependent argument expressions used in a call. In the example f((T)0), f is a dependent name. In the Standard, this is specified at (14.6.2/1).

Additional notes and examples

In enough cases we need both of typename and template. Your code should look like the following

template <typename T, typename Tail>
struct UnionNode : public Tail {
    // ...
    template<typename U> struct inUnion {
        typedef typename Tail::template inUnion<U> dummy;
    };
    // ...
};

The keyword template doesn't always have to appear in the last part of a name. It can appear in the middle before a class name that's used as a scope, like in the following example

typename t::template iterator<int>::value_type v;

In some cases, the keywords are forbidden, as detailed below

  • On the name of a dependent base class you are not allowed to write typename. It's assumed that the name given is a class type name. This is true for both names in the base-class list and the constructor initializer list:

     template <typename T>
     struct derive_from_Has_type : /* typename */ SomeBase<T>::type 
     { };
    

  • In using-declarations it's not possible to use template after the last ::, and the C++ committee said not to work on a solution.

     template <typename T>
     struct derive_from_Has_type : SomeBase<T> {
        using SomeBase<T>::template type; // error
        using typename SomeBase<T>::type; // typename *is* allowed
     };
    

这篇关于我必须在哪里以及为什么要放置“模板"?和“类型名称"关键词?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆