6.8 歧义消解
6.8 歧义消解
1、在包含表达式语句和声明的语法中存有歧义:以函数式显示类型转换(
T(a)->m = 7; // expression-statement
T(a)++; //expression-statement
T(a,5)<<c; //expression-statement
T(*d)(int); //declaration
T(e)[5]; //declaration
T(f) = { 1, 2 }; // declaration
T(*g)(double(3)); // declaration
在例子的最后,指向T的指针g被初始化为double(3)。在语义上这无疑是非法的,但这并不影响句法分析。
2、下面的例子都是声明。
class T {
// ...
public:
T();
T(int);
T(int, int);
};
T(a); //declaration
T(*b)(); //declaration
T(c)=7; //declaration
T(d),e,f=3; //declaration
extern int h;
T(g)(h,2); //declaration
]
3、上述歧义消解方法是纯句法上的;也就是说,在此类声明中出现的名字的意义,不论它们是否为类型名,均不会被考虑或改变(vc6与此不同,它会根据名字的语义信息来消歧,详见下面的论述)。必要的时候可对类模板进行实例化以确定一个限定名是否为type-name(在以前的标准中,消歧过程是不会对类模板进行实例化的)。消歧过程先于句法分析进行,被消歧过程解释为声明的语句可能是一个非法的声明。如果在句法分析过程中,位于模板参数中的名字的约束集不同于其在测试过程(消歧过程)中的约束集,那么程序是非法的。编译器不需要对此进行诊断。[注:这只发生在名字已被早先的声明声明过的情况下。因为消歧过程并不关心名字是否被声明,它不会利用名字的当前信息]例如:
struct T1 {
T1 operator()(int x) { return T1(x); }
int operator=(int x) { return x; }
T1(int) { }
};
struct T2 { T2(int){ } };
int a, (*(*b)(T2))(int), c, d;
void f() {
// disambiguation requires this to be parsed
// as a declaration
T1(a) = 3,
T2(4), // T2 will be declared as
(*(*b)(T2(c)))(int(d)); // a variable of type T1
// but this will not allow
// the last part of the
// declaration to parse
// properly since it depends
// on T2 being a type-name
}
(我是这样理解函数f中的代码的:首先是消歧过程对代码进行分析,消歧过程尝试将语句理解为声明,分析到T2的时候,按照标准的要求,它不会检查T2的语义信息,所以消歧过程不知道T2是一个类型名,它只认为T2是一个T1类型的变量,并且初始化为4;按照标准,消歧过程这时也不会改变T2的语义信息为变量,所以在符号表中T2仍然为类型名;所以接下来的分析能顺利的将(*(*b)(T2(c)))(int(d))理解为函数声明。所以在纯句法上,上述语句是一个合法的声明。消歧过程最后通知句法分析按声明分析当前语句,句法分析过程分析到T2的时候,将T2的语义信息置为类型为T1的变量,故而在分析接下来的函数声明时,将无法正确解析T2(c),因而报出错信息。
然而在vc6中,上述代码是能够通过编译的。但上述代码并没有被解释为声明,而是被解释为一逗号表达式,所以vc6的消歧过程要么利用了名字的语义信息,要么便是优先将语句解释为表达式。)