本节书摘来自异步社区出版社《C++覆辙录》一书中的第2章,第2.5节,作者: 【美】Stephen C. Dewhurst(史蒂芬 C. 杜赫斯特),更多章节内容可以访问云栖社区“异步社区”公众号查看。
2.5:取大优先解析原则带来的问题
当面对如下表达式时,你何以措手足?
++++p->*mp;```
你可曾有幸和“中士运算符”17打过交道?
template
class R{
// ...
friend ostream &operator <<< // 一个“中士运算符”?
T>(ostream &, const R&);
};`
你可曾为“下面的表达式是否合法”的问题迟疑过?
a+++++b
欢迎进入取大优先解析原则的世界!在C++源代码被编译的早期阶段,编译器中负责“词法分析”的部分有一项任务,就是把源码输入流打碎成一个个地“单词”,或曰“词法单位”。当词法分析过程遇到一个形如“->”的字符序列时,它可以同样合理地把它解释成3个词法单位(“-”、“>”
和“”)、2个词法单位(“->”和“*”)
或是单个1个的词法单位(“->*”
)`。为了摆脱此类多义性的局面,词法分析引入了取大优先解析原则,也就是总是能取多长的可以作为词法单位的字符序列就取多长:取大优先嘛。
表达式“a+++++b
”是非法的,因为它被解析成了“a++ ++ +b
”,但对像“a++”这样的右值应用后置自增运算符是非法的。如果你想把一个后置自增的a和一个前置自增b的相加,你至少要加一个空格:“a+++ ++b”
。如果你哪怕考虑到了你的维护工程师一点点,你就会再加一个空格,尽管严格说来不是必要的:“a++ + ++b”
。多加几个括号的话,也实在不会有谁抱怨你什么:“(a++) + (++b)
”。
取大优先解析原则除了在两种常见情况下,多数都是作为问题解决者而不是制造者的形象出现。不过在这两种情况下,的确令人生厌。第一种情况是用一个模板具现化的结果型别来具现化另一个模板。举例来说,采用标准库里的元素的话,某软件工程师打算声明一个list,其元素是以string对象为元素的vector容器:
list<vector<string>> lovos; // 错误! ```
倒霉的是,在具现语法里两个相毗邻的右半个尖括号被解释成了一个右移位运算符,于是我们就犯了一个语法错误。空格在这种情况下是非加不可的:
`
list<vector<string> > lovos;`
另一种情况是为指针型别的形参给予默认初始化值的时候:
`
void process(const char*=0); // 错误! `
这个声明企图在形参列表里使用运算符*=。语法错误。这种错误属于“自作孽,不可活”——如果代码作者记得给形参一个名字,就根本不会犯这种错误。现在你明白了,给予形参名字不仅起了“自注释”的作用,同时也让取大优先解析原则带来的问题消弭于未现:
void process(const char *processId = 0);`