From 83bc7817c5fd00d361ed11768c54e309738b8724 Mon Sep 17 00:00:00 2001 From: LH_Mouse Date: Fri, 9 Jun 2017 01:10:19 +0800 Subject: [PATCH] Elaborate the cause of emergence of `typename` before dependent names that designate types. --- ReadMe.md | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/ReadMe.md b/ReadMe.md index 10af4f0..09f9c09 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -1613,7 +1613,24 @@ template struct Y 事实上,标准对`typename`的使用规定极为复杂,也算是整个模板中的难点之一。如果想了解所有的标准,需要阅读标准14.6节下2-7条,以及14.6.2.1第一条中对于`current instantiation`的解释。 -简单来说,如果编译器能在出现的时候知道它的类型,那么就不需要`typename`,如果必须要到实例化的时候才能知道它是不是合法,那么定义的时候就把这个名称作为变量而不是类型。 +简单来说,如果编译器能在出现的时候知道它是一个类型,那么就不需要`typename`,如果必须要到实例化的时候才能知道它是不是合法,那么定义的时候就把这个名称作为变量而不是类型。 + +我们用一行代码来说明这个问题: + +```C++ +a * b; +``` + +在没有模板的情况下,这个语句有两种可能的意思:如果`a`是一个类型,这就是定义了一个指针`b`,它拥有类型`a*`;如果`a`是一个对象或引用,这就是计算一个表达式`a*b`,虽然结果并没有保存下来。可是如果上面的`a`是模板参数的成员,会发生什么呢? + +```C++ +template void meow() +{ +    T::a * b; // 这是指针定义还是表达式语句? +} +``` + +编译器对模板进行语法检查的时候,必须要知道上面那一行到底是个什么——这当然可以推迟到实例化的时候进行(比如VC,这也是上面说过VC可以不加`typename`的原因),不过那是另一个故事了——显然在模板定义的时候,编译器并不能妄断。因此,C++标准规定,在没有`typename`约束的情况下认为这里`T::a`不是类型,因此`T::a * b;` 会被当作表达式语句(例如乘法);而为了告诉编译器这是一个指针的定义,我们必须在`T::a`之前加上`typename`关键字,告诉编译器`T::a`是一个类型,这样整个语句才能符合指针定义的语法。 在这里,我举几个例子帮助大家理解`typename`的用法,这几个例子已经足以涵盖日常使用[(预览)][3]: