2.6:声明饰词次序的小聪明
就语言本身所限,声明饰词孰先孰后纯属无关紧要的形而上之争:
`
int const extern size = 1024; // 合法,但有离奇不经之嫌 `
无论如何,如果没有令人信服的理由去背离习惯用法,那么顶好还是接受有关声明饰词次序事实上的标准:先写连接饰词,再写量化饰词,再写型别。
extern const int size = 1024; // 正常下面这个指针的型别是什么呀?
int const *ptr = &size; ```
对,这是一个指涉到常量整数型别的指针。但你根本难以置信有多少软件工程师会把它误读成一个指涉到一般整数型别的常量指针18:
`
int * const ptr2 = &size; // 错误! `
以上是两种完全不同的型别。当然了,第一种指针型别可以指涉到一个常量整数型别,第二种不行19。很多软件工程师会随口把“指涉到常量型别的指针”念成“常量指针”,这不是一个好习惯,它只会把你要表达的真实意思(“指涉到常量型别的指针”)传达给那些粗枝大叶之徒,而真正字斟句酌的称职后生则会被你的言辞误导(理解成“指涉到一般型别的常量指针”)。
当然需要承认的是,标准库里有一个字面上表示“常量迭代器”之意的`const_iterator`概念,它无可救药地实际上表示一个“指涉到常量元素的迭代器”,而这些迭代器自身却并不具常量性(标准委员会的家伙们某天吃错了药不是你要向他们学坏的理由)。仔细区分“指涉到常量型别的指针”和“常量指针”(参见常见错误31)。
由于声明饰词次序在技术层面上无关紧要,一个指涉到常量的指针可以以两种形式声明:
const int *pci1;
int const *pci2; `
有些C++专家比较提倡第二种书写形式,因为他们认为对于更复杂的指针型别声明来说,这种写法更容易读:
int const * const * pp1; ```
把量化饰词const置于若干声明饰词的最后,这样我们就可以倒过来读所有的指针型别的饰词。从右到左,pp1的指涉物是一个常量(`const`)指针,后者指涉到一个整数常量(`const int`)。而习惯的饰词次序则不支持这样的平凡规则:
const int const pp2; // pp2的型别和pp1完全相同⑦`
⑦译者注:本书作者显然在后来的岁月里改变了他的有关饰词次序的看法,变得更加包容。他在本书里把两种饰词的次序中的一个打上了“不推荐”的烙印,显然觉得两种用法里一种优于另一种。但他在另一本比较晚近出版的书中谈到这个问题时,就表示两种用法选择哪一种“无关紧要”了。参见(Dewhurst, 2006
),条款7。而Scott Meyers
在谈到量化饰词应该放在型别前还是后时,则更直截了当地说“你应该同时适应两者”,参见(Meyers, 2006),条款3。根据这些材料及其变迁的历史轨迹,我们可以说,读别人的代码时,不应该误读,而自己在撰写代码时则纯属风格问题,可以根据自己的理解方向和喜好来选择一种,并固定下来,在编码实践中沉淀为自己的代码风格的一部分。
前一种饰词次序的安排也没有带来太多复杂性,一个C++维护工程师若是在要阅读和维护的代码里存在这样的片段,他也应该是有能力搞定的。更重要的是,指涉到指针的指针或是其他类似的声明是较少见的,尤其少见于交由C++新手打理的接口代码里。典型情况是,它们藏匿于基础实现码的深处。平凡的、直截了当的指涉到常量的指针就常见得多。所以,还是遵循习惯用法以避免误解较佳:
const int *pci1; // 正确:指涉到常量的指针