0.简介
大多数编程语言编译器会进行两项基本任务:
1)tokenization,又称为扫描scanning
2)parsing分析,上面的tokenization步骤会把源码读入的一个字符序列characters sequence,
再由parsing,也就是词法分析器,在词汇单元序列中寻找已知的模式patterns。
1. NonTemplate的上下文敏感性(context Sensitivity)
tokenizing要比Parsing容易,幸运的是parsing理论基础已经相当稳固,很多语言都可以根据这个理论不困难地进行parsing。这个理论对于上下文无关地语言最有效,然而C++是一个上下文敏感的语言。为了进行Parsing, C++编译器把一个符合表(symbol table)结合于tokenizer和parser身上:当某个声明被成功解析后,便进入符合表中。当tokenizer找到一个标识符identifier,便查询符号表,如果在其中找到对应(同名)类型,就将resulting token标注出来。
举个例子,如果C++编译器看到
x*
于是tokenizer查询x。如果在符合表中找到类型x,parser会看到
identifier, type,
x symbol, *
于是推断位一个声明的开始,然而如果x不是类型,那么parser会从tokenizer获得:
identifier, nontype,
x symbol, *
于是这个构件construct就只能被合法解析为一个乘法操作。这些原则的细节取决于具体操作策略,但要旨没变。
下面例子说明上下文敏感性context sensitivity。考虑以下表达式:
x<1>(0)
如果x是个class template名称,这个表达式便是将整数0转型为X<1>所指涉的类型。但如果X不是个template。这个表达式等价于:
(X<1 ) > 0
换句话说X和1的比较结果(true或false)被隐式转型为1或0,然后再与0比较。虽然这种写法很少见,但它确实是合法的C++程序代码。只有C++ parser看到一个template名称时,它才确实是合法C++代码。
2. 类型的受控名称Dependent Names
3. 模板的受控名称Dependent Names
4 Using声明语句中的受控名称Dependent Names
5. ADL和 显式模板实参Explicit Template Argument