??
在本系列的上一篇文章里,我列出了我認為最重要的五本
C++
圖書,但大量有關
C++
的重要文獻并非來自圖書。比如期刊、雜志、網絡上的文章;博士論文、會議紀要;新聞組帖子;博客;標準化文檔等很多很多。它們對
C++
的進步與繁榮作出了巨大貢獻。我沒有讀全,甚至談不上讀了大多數,但作為
C++
的長期關注者,我還是閱讀了很多這類文獻。在本期里,我將評選
C++
歷史上最重要的五部非圖書類文獻。和上期評選圖書一樣,我仍然將數量限制為五,盡管我沒有寫出過重要到能上這個榜的東西,但仍然將自己列入了候選隊伍。以下文獻按時間為序。
一個讓我無可回避的邏輯范疇兩難問題是:如果文獻
A
的思想對
C++
直接影響很小,但文獻
B
的作者讀到了
A
,將這個思想引入了
B
并產生了巨大影響,那么到底哪個文獻更重要,
A
(“發明者”)還是
B
(“繁榮者”)?我最終選擇了
B
,并不是因為這種做法天然就正確,而是因為我不想花力氣拼命追查下列文獻作者的思想是否從別的文獻繼承而來
[
注釋
1]
。反過來,我隨便翻到
C++
語言規范的某頁。大家知道,
const member functions
里的
const
是不徹底的:指針數據成員自動變為
const
,但指針所指的數據本身不會。借鑒這個規定,我假設公布在下面的名單里的文獻重要,而忽略它們引用的基礎物(以及我所不知的其他文獻)。當然,從
C++
本身來說,這可能不是正確的做法,但易于實現,所以我也這么干了
[
注釋
2]
。
在詳細解說名單之前,我想請各位發發善心,允許我為《
C++ Report
》——曾經為
C++
作出了最重大貢獻的期刊——說幾句悼詞(如果你不那么仁慈,就請直接跳過去閱讀后面內容吧)。在其存在的大部分時期(
1989-2000
)里,《
C++ Report
》一直是
C++
推動者和鼓吹者紙張寫作的樂園(在此期間,首倡電子寫作的是
Usenet
新聞組
comp.lang.c++
,后來還有
comp.std.c++
和
comp.lang.c++.moderated
)。在《
C++ Report
》發表文章的人里,有些名字你可能聽說過(比如我上期列出的“最重要的
C++
圖書”的作者),更多的可能你就不知道了(譬如下面要提到的一些文獻的作者,以及——算了,名單太長,簡直沒辦法開始。我知道,如果開了頭列出一些名字,那就暗示著未列出的人沒列出的那么重要,這樣一來我的麻煩就大了。所以干脆一個不提,但請相信我,《
C++ Report
》有生之年一直相當興盛,它吸引了這個領域最好的寫作群體——都是最有興趣,也是最有實力去寫作的人)。《
C++ Report
》關門的時候,很多專欄作家投向了《
C/C++ Users Journal
》,但這個雜志從來沒有像《
C++ Report
》那樣吸引過我;現在,
CUJ
也停刊了。《
C++ Report
》留給像我這樣整天胡說八道的老怪物們的,就只有面對時光飛逝的無奈哀嘆了。
嘮叨這么多,我感覺好點了,還是繼續說我的最重要非圖書類文獻名單吧:
《
Programming in C++, Rules and Recommendations
》,作者是
Ellemtel
電信系統實驗室的
Mats Henricson
和
Erik Nyquist
,
1992
年。在我
90
年代早期前后的一些文章里,我提到過當時很多程序員渴求如何駕馭
C++
威力方面的指導意見,他們最感興趣的是告訴他們該做什么、不該做什么的編程引導手冊。幾乎在我的《
Effective C++
》嘗試提供這方面指導的同時,
Mats Henricson
和
Erik Nyquist
在互聯網上發布了他們寫的編程手冊。其實在此之前,這本手冊就出來一段時間了,但因為是瑞典文,所以大大限制了它的傳播。
Ellemtel
版手冊以技術性語言寫成,容易閱讀,因此二位作者聲名遠播、影響很大。不久,大家得知他們準備成書出版,此時我就有不祥預感(競爭于市場可能是件好事,但那時,我是這個市場上僅有的參與者。我真的很喜歡這本書冊的風格
[
注釋
3]
)。我們對這本書滿懷期待,轉眼間時間過去了幾年。
1996
年底,它終于面世了(《
Industrial Strength C++
》,
Mats Henricson
和
Erik Nyquist, Prentice Hall, 1997
),但那個時候,這本書的很多指導意見與同時代的編譯器相比已經過時,它包含的很多信息在
C++
社區里已經廣為人知,與
4
年前第一次的英文版相比,人們感覺它的作用已經大打折扣。我閱讀了這本書,先是興趣滿懷,然后就有點傷感,因為我感覺到花費
4
年時光從互聯網文檔到成書,不僅它包含的技術信息失去了當年的光芒,寫作本身也喪失了原有的精神。我估計原稿已經被無數次修改,以期符合評審者在各方面的要求。這就解釋了它為什么花費了如此長時間才得以出版,為什么最后的成書如此平淡。
我想,作者和出版商對這本書寄予厚望,但事與愿違,不過這并不能削弱最初互聯網版本的影響力。它一出來,
C++
程序員就一口咬了上去。它是
C++
最佳實踐規范總結道路上的重要里程碑。
《
Exception Handling: A False Sense of Security
》,作者
Tom Cargill
,
1994
年發表于《
C++ Report
》
11
、
12
月刊。
1994
年,
C++
社區矯矜之氣彌漫。當時,
C++
是很熱門的語言,工作崗位充足,很多人認為
C++
無所不能。在此前幾年里,這門語言里增加很多重要的特性,比如多繼承、模板,以及稍晚點的異常。因為異常是新事物,《
C++ Report
》上就出現了很多討論文章,凡是
C++
程序員出現的場合,大家都是三句話不離異常。很多文章反映了屬于那個時代的狂熱:“異常美妙之極,它們讓錯誤處理變得簡單。你需要做的所有事情就是去理解
try
、
throw
和
catch
。看我編寫一個堆棧類吧,告訴你們
C++
有了異常處理后,將比過去酷多少。”
Tom Cargill
的文章(是他的長期專欄
“C++ Gadfly”
——這是多年來最名副其實的專欄之一——里的一篇)拂去了我們臉上集體自滿的微笑。僅僅用一句話,
Cargill
就說明了
try
、
throw
和
catch
對這個問題毫無幫助:
運用異常的真正難點在于如何以如下方式編寫所有介于二者(
thow
和
catch
)之間的代碼:任何異常都能從
throw
處安全到達處理它的地方,且不破壞傳遞路線上的其他程序部分。
這個專欄繼續剖析了《
C++ Report
》上我剛才提到過的“異常美妙之極”系列專欄上的文章
[
注釋
4]
,最后以一個擂臺(
Cargill
稱之為“邀請”)結束:發布一個異常安全的堆棧類。這個挑戰引來了潮水般的回應,但我認為,直到
1997
年,
Herb Sutter
發表的一篇文章才算真正有分量(后面會說到這個事情)。
我認為
Tom Cargill
的專欄文章不僅證明了我們對于異常想法的幼稚,而且也還了
C++
一個清白。在我們明白如何編寫異常安全的代碼時,
Java
這門可愛的新語言出現了(就我所知,現在是
Ruby on Rails
),曾經信誓旦旦的“我們天下第一”狂想再也沒有回來過。
?
《
Curiously Recurring Template Patterns
》,
Jim Coplien
于
1995
年發表在《
C++ Report
》
2
月刊。這篇文章的意義不在于它的內容本身,而在于它給所述內容的命名。真是雙重巧合啊,這篇文章來自于
Coplien
的專欄“
The Column Without a Name
”,而且他給文章起的名字也已經直接成了一個模式名:
The Curiously Recurring Template Pattern (CRTP)[
注釋
5]
。這個模式本身是指將派生類作為參數在它自己的模板化基類里使用:
?
template<typename T>
class Base { ... };
?
class Derived: public Base<Derived>
{ ... };
?
很多人對模板的使用都超出了
T
容器的范圍,最后往往皈依到了
CRTP
的設計思想。這時候,他們通常都會懷疑這樣的代碼是否可以通過編譯,當發現可以通過編譯(可能他們大吃了一驚)后,就很擔心自己弄出這樣的設計,是否是頭腦癡呆的早期癥狀。就在這時,
Coplien
投稿了。更多有經驗的同事會保證說:“不是啊,你沒有精神病。從基類派生一個類,基類又在派生類的基礎上模板化,不僅是合法的設計技術,而且它還有一個正式的名字呢:
the Curiously Recurring Template Pattern
。”
《
Using C++ Template Metaprograms
》,作者
Todd Veldhuizen
,《
C++ Report
》
1995
年
5
月。此文見證了
template metaprogramming (TMP)
的第一次大規模出現熱潮。這是一個重要的時期,但我完全錯過了這條船。我清楚記得閱讀這篇文章時我的想法:“好,這樣你就可以用遞歸的實例化模板去模擬編譯時的循環。你能通過模板特化去實現編譯時的
switch
表達式。太好了!但你為什么要這樣做?”哦,我說謊了。我當時真正的想法是:“但你如果這樣做,你就是個瘋子。”
[
注釋
6]
盡管
C++
社區并不缺乏追捧,再后來有關
TMP
的文章也在各種論壇上陸續出現,不過這不讓我驚詫。我那時深信
TMP
是一門概念過于離奇、語法過于超前以至于沒有立足點的技術。現在,我知道大多數人似乎也同意它在語法上的確讓人感覺不適,概念上不說過于怪異,至少也偏離了主流思想,這讓我得到了些許安慰。但很清楚,它獲得的支持日益增多,已經成為每個庫程序員技巧包里的重要工具。為了彌補
Veldhuizen
首次描述它時我對其重要性嚴重低估的過失,我在《
Effective C++
》第三版的一個條款里盡我全力總結了這門技術及其用途。
《
Exception-Safety in Generic Components
》,作者
David Abrahams
。我能找到的最早的是發表在德國《
Dagstuhl Castle
》
1998
年
4
月
27
日到
5
月
1
日的《
Proceedings of the International Seminar on Generic Programming
》。不過我想在
1997
年中,相關文獻可能就出現了,因為
Herb Sutter
發表在《
C++ Report
》(也許是別的雜志)
1997
年
9
月刊的一篇文章引用了
David Abrahams
論文里的內容。我也發現有對
David Abrahams
于
1997
年
4
月發表在
Usenet
上的文章(
http://tinyurl.com/nk5vn
)里內容的引用,不幸的是,這個鏈接已經實效。
現在,保證函數提供基本、強健和無拋出的三個異常安全辦法已經廣為人知。
Herb Sutter
傳播了這些術語,并且寫了很多相關的文章,但最早提出它們的是
David Abrahams
,我這么說的主要理由是
Sutter
已經很細心地承認過這一點
[
注釋
7]
。一些重要文獻初生于相當狹小的空間,但讀者中間很多人有條件發揮廣泛影響力(比如正在定義
C++
標準,或者負責標準庫的實現),因此文獻的影響力將會被大大提高。我想,這就是一個例子。
有趣的是,盡管
Abrahams
的文獻啟蒙了我們對奠定
C++
標準庫規范基礎的異常安全的理解,但
C++
標準里卻未提及“
basic guarantee
”、“
strong guarantee
”或是“
nothrow guarantee
”。
??
注釋:
1.
因為傳遞環路問題(比如
B
的想法起源于
A
,但
A
的靈感來自于
Z
,
Z
又是受
Y
的影響……),實際情況比這還要糟糕。
??????? 2. const
不徹底的這個理由可能和簡化實現并不相干。其實考察
const
和指針結合時的常見規則,它就是一個很自然的結果。我們定義指針
p
為
const
,但這并不能限定
*p
也是
const
。在
const member function
里,
*this
是
const
,如果
p
是
*this
的一個指針成員,我們也不能確定
*p
就是
const
。
??????? 3.
稍早一點,還有另一本
C++
書冊類圖書,即
Thomas Plum
與
Dan Saks
合著的《
C++ Programming Guidelines
》,
1991
年由
Plum Hall
出版,但它從未引起過大量關注。我很早就讀過這本書,現在我又快速閱讀了一遍,給它的結論是:嗯……相當無趣。
???? ?? 4.
我是《
C++ Report
》的專欄作家,我記得那時候就在想,
Cargill
肯定瞄上了我,要我去寫異常方面的東西。系統且有專業眼光地挑出同僚作品中的缺點,是我曾經經歷過的最費心勞神的事情。我確信從此以后,自己不是發表作品前要再三檢查的唯一專欄作家了。
???? ? ?5.
正在布朗大學計算機科學系攻讀博士學位的
Andrei Alexandrescu
曾經公開指出這個模式的名字應該被還以本色,比如“
F-bounded polymorphism
”,但很不幸,他的意見沒有引起人們的注意。這件事引起了我的興趣,因為我就在那兒獲得了博士學位,不過我從未聽說過“
F-bounded polymorphism
”。
CRTP
盡管是一個古老的名字,但仍然能打動我,因為它比“
F-bounded XXX
”顯得更自然。
??????? 6.
它可能對文獻里第一個例子實現編譯時冒泡排序沒有什么用處。我對冒泡排序一直有相當病態的反感。不僅是因為這種排序算法幾乎從來就不適用于我的工作,而且也幾乎從來不值得在工作中考慮這種算法。哦,我離題了。
??????? 7.
初時,這點還不完全清楚。
Sutter
在《
C++ Report
》
1997
年
9
月刊上發表的文章里并沒有將“
basic guarantee
”和“
strong guarantee
”歸功于
Abrahams
,但他提供并允許放在我的
1999
版《
Effective C++
》光盤的文章提到了
Abrahams
。我有十足把握公開說《
C++ Report
》發表的文章和
Sutter
提供給我的文章是不一樣的。
?