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

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

问题描述

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

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

我有以下代码:

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> {
    };
};

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

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?

推荐答案

(参见 这里也是我的 C++11 答案)

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

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;

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

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.

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

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:

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

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.

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

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):

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

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.

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

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;

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

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.

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

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;

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

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; 
}

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

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):

在名称查找(3.4)之后发现一个名称是一个模板名称,如果这个名称后面跟着一个<,则<是始终作为模板参数列表的开头,而从不作为名称后跟小于操作员.

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.

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

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:

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

大多数规则都是直观的,并且是递归构建的:例如,如果 N 是值,则构造为 T[N] 的类型是依赖类型-依赖表达式或 T 是依赖类型.详细信息可以在依赖类型的 (14.6.2/1) 部分、类型依赖表达式的 (14.6.2.2)(14.6.2.3) 用于依赖值的表达式.

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.

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

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:

名称是使用标识符 (2.11)、运算符功能 ID (13.5)、转换功能 ID (12.3.2) 或模板 ID (14.2) 来表示实体或标签 (6.6).4, 6.1)

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)

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

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.

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

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.

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

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).

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

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;
    };
    // ...
};

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

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

  • 在依赖基类的名称上不允许写typename.假定给定的名称是类类型名称.对于基类列表和构造函数初始值设定项列表中的名称都是如此:

  • 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 
 { };

  • 在使用声明中,不能在最后一个 :: 之后使用 template,并且 C++ 委员会 不致力于解决方案.

  • 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天全站免登陆