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

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

问题描述

在模板中,我必须在依赖名称上放置 typename 模板什么是依赖名称?我有以下代码:

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

我遇到的问题是在 typedef Tail :: inUnion& dummy 行。我相当肯定, inUnion 是一个依赖的名称,VC ++是非常正确的窒息它。我也知道,我应该能够添加模板在某处告诉编译器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 ++程序,编译器需要知道某些名称是否是类型。以下示例演示:

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: :x 是指如果 t 是指模板类型参数? 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

对于名称,存在类似的错误

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

这实际上是一个有效的表达式!它使用less-than运算符将 boost :: function 与零( int())进行比较,然后使用大于运算符将比较结果 bool f 。但你可能知道, 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 相同的问题。如果我们还不能知道在解析代码时该名称是否是模板?我们需要在 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

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

大多数规则是直观的,并且递归地构建:例如,构造为 T [N] 的类型是依赖类型如果 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),operator-function-id表示实体或标签(6.6.4,6.1)的代码(13.5),转换函数标识符(12.3.2)或模板标识符(14.2)

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)

标识符只是一个普通的字符/数字序列,而接下来的两个是运算符+ 运算符类型表单。最后一个形式是 template-name< argument list> 。所有这些都是名称,并且通过在标准中的常规使用,名称也可以包括限定符,它们说明应该在什么命名空间或类中查找名称。

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

在足够的情况下,我们需要 typename 模板。您的代码应如下所示:

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


  • 在依赖基类的名称上,不允许写 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 
 { };


  • 在使用声明中不能使用 code>之后 :: 和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天全站免登陆