來源:陳良喬
C++0x作為C++的下一個國際標準,已經在業界熱炒多年。但是,儘管業界對這個新標準千呼萬喚,她總是不願意過早地來到我們面前。在最近一次CodeGuru對C++之父Bjarne Stroustrup博士的採訪中,C++之父終於給我們帶來了好訊息——C++0x的標準化工作已經接近尾聲,C++0x呼之欲出。下面是整個採訪過程的節選,我們可以通過這個訪談,掀起C++0x的蓋頭來,瞭解C++0x的最新進展,新的特性以及未來的計劃。
Danny Kalev: C++0x的標準化過程進展如何?我們現在有多麼接近這個新的C++標準?
Bjarne Stroustrup: 我們計劃在2011年3月26日進行最終的技術投票。雖然其後還會進行正式的國家投票以及ISO官僚主義的拖延,但是我十分相信,我們會在2011年使這個官方的標準來到我們面前。
Danny Kalev: 在所有關於C++0x的核心特性以及庫的改進中,C++0x為一個典型的C++程式設計師帶來了哪些好東西?C++0x的哪些方面使您特別驕傲?最後,鑑於目前市面上還缺少關於C++0x的相關教材和資料,對於程式設計師們學習和使用C++0x的新特性您有什麼建議?
Bjarne Stroustrup: C++0x對於C++的改進是以許多小的語言特性的改進以及部分新特性的增加的形勢出現的,並不是對C++革命性的更新。我猜想,許多改進對於大多數人而言並不是十分重要的,但是會讓C++變成一門更好的程式設計語言。但是這一點也並不會影響我的核心觀點:C++0x的改進彌散地分佈在C++語言的各個部分;它們在許多地方以多種形式改善我們的C++程式碼;並不像人們通常理解的改進一樣,被隔離成某一個獨立的新增加的元件。更形象地說,我認為C++0x的改進就像我們獲得了很多新的種類的磚塊,這樣我們可以構建很多以前無法輕鬆構建的建築,並且更加容易和靈活優雅。實際上,使用C++0x,我總是能夠寫出比使用C++98更加簡單更加優雅的程式,並且,通常也會有更高的效能。
當我們首先來看幾個可以讓C++程式設計師的生活更加輕鬆愜意的C++0x改進。考慮下面這段程式碼:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
void f(vector<pair<string,int>>& vp) { struct is_key { string s; bool operator()(const pair<string,int>&p) { return p.first == s; } }; auto p = find_if(vp.begin(), vp.end(), is_key{"simple"}); // … } |
這段程式碼看起來並沒有什麼激動人心的新改進,但是我要指出其中有四個小特性是C++98所不具備的:
1、在“vector<pair<string,int>>”中,第一個“>”和第二個“>”之間並沒有空格,我認為這是C++0x中最小的改進,但是卻可以省去程式設計師們新增空格的繁瑣。
2、我定義了一個變數p但是並沒有明確地指出它的資料型別,作為替代,我使用了auto作為其資料型別,這就意味著“使用初始器(initializer)的資料型別”,所以p的資料型別就是vector<pair<string,int>>::iterator。這將節省程式設計師編碼以及除錯可能出現的Bug的時間。這是C++0x最“老”的新特性,我在1983年就實現了這個特性,但是因為一些相容性問題,這一特性一直沒有被納入C++標準。
3、區域性結構體is_key被用作模板引數型別,也許你並沒有注意到這一點,但是這樣的用法在C++98中是非法的。
4、最後,我使用初始器{“simple”}建立了一個鍵。在C++98中,我們只能夠以這樣的方式初始化一個變數而不能初始化一個函式引數。C++0x通過“{…}”操作符,提供了一致的初始化方式。
我們還可以利用Lambda表示式進一步簡化這個例子:
1 2 3 4 5 6 7 8 9 |
void f(vector<pair<string,int>>& vp) { auto p = find_if(vp.begin(), vp.end(), [](const pair<string,int>&p) { return p.first=="simple"; }); // … } |
Lambda表示式是對函式物件的定義和使用的一種簡化。這裡,我們使用Lambda表示式簡單地表示了find_if()演算法的謂語使用pair作為引數並將其第一個元素與“simple”進行比較。
是的,這些主要的新特性都很好,但是C++程式設計師們所關心的那些很重要的問題呢?
▲傳統的“threads-and-locks”風格的系統級平行計算的型別安全得到了支援。和一個可以用於無鎖(lock-free)程式設計的新的記憶體模型一起,它們將共同為C++程式設計師們編寫更加高效,更具備可移植性的平行計算程式提供強有力的支援。
▲C++0x提供了一個更高抽象層次的平行計算模型。這個平行計算模型基於非同步地執行多個任務,而這些任務之間又是通過所謂的訊息緩衝(message buffer)進行通訊的。
▲一個新的正規表示式標準庫元件
▲雜湊容器
▲移動語義以及移動語義在標準庫中的應用。特別地,現在我們可以以傳值的方式從函式返回一個體積比較大的物件。例如:
1 2 3 4 5 6 7 |
vector<int> make_vec(int n) { vector<int> res; for (int i=0; i<n; ++i) res[i] = rand_int(0,100000); return res; } |
標準庫中的vector有一個移動建構函式(move constructor),它可以接受一個右值並簡單地直接將其轉換為目標物件,而不是複製容器中的所有元素來完成物件的建立。這就表示函式的返回可以通過簡單的少數幾次賦值完成,而不再是通過更多的,比如一百萬次,逐個複製元素來完成函式的返回。這樣,我們無需再使用繁瑣而危險的指標,引用,記憶體的申請和釋放等。移動語義為我們傳遞大體積的物件提供了一個全新的完整的解決方案。特別地,它的實現也非常簡單,並且使得對於資料的操作更加富有效率,比如兩個矩陣的乘法操作:
1 |
Matrix operator*(const Matrix&, const Matrix&); |
我可以一直繼續下去,但是那將是一個長長的列表,但是這也將我們引向了更有趣的第二個問題:人們應該如何學習和使用C++0x中的這些新特性?我正在寫第四版的《C++程式語言》(4th edition of The C++ Programming Language),但是那還有很多工作要做,那將會花費超過一年的時間。我想一定有其他的技術作者正在寫或者正打算寫關於C++0x的書,但是專家們或者C++的初學者想要找到比較好的書以及技術參考資料,恐怕還要等一段時間。(譯註:我正在寫一本全面覆蓋C++0x新特性的C++參考書《我的第一本C++書》,即將由華中科技大學出版社出版,敬請期待)幸運的是,現在已經有一些關於C++0x的早期技術資料了,比如我的C++0x FAQ,它提供了很多簡短的例子,以展示C++0x的核心特性,標準庫的改建以及其它現在可以使用特性。但是,我們還需要更多的FAQ以及線上文件。我們還需要一些成系統地解釋如何使用C++0x的新特性從而更好地支援C++的開發的資料。基於這樣的考慮,我們需要的應該是一本書。
當我在寫程式的時候:使用C++的原則和實踐經驗,並假設我正在使用的程式語言是C++0x,但是,如果不使用C++0x的新特性那將是一件非常痛苦的事情。我可以十分肯定地預言,對於C++的培訓者和學習者,C++0x將是上天的恩賜。C++0x對於對於一些好的程式設計技術和風格提供了大量的更有力的支援。比如,現在我們有一個普遍一致的初始化機制,現在我們都統一使用“{…}”操作符完成變數的初始化工作,並且不管我們在什麼地方使用“{v}”初始化一個變數X,我們都會得到相同的結果。對於C++98中不一致的初始化形式,“=v”、“={v}”和“(v)”,這是一個非常大的改進。
1 2 3 4 5 6 7 8 |
vector<double> v = { 1,2,3,4}; // a user-defined type double a[] = { 1,2,3,4}; // an aggregate int f(const vector<double>&); int x = f({1,2,3,4}); auto p = new vector<double>{1,2,3,4}; struct S { double a, b; }; S s1{1,2}; // has no constructor complex<double> z { 1,2,}; // has constructor |
在我們從C++0x的簡化中獲得好處之前,也許我們會經歷這樣一個黑暗的時期——很多人會通過列舉C++0x的新的語法規則或者是孔乙己式地深究C++0x的語法細節來展示自己的“聰明才智”。實際上,這樣做是有害的。
我們不能指望人們僅僅通過閱讀就能對C++0x程式設計有一個很好的理解。人們必須在開發實踐中真正地使用這些新特性。幸運的是,C++0x的很多新特性已經在很多編譯器(例如,GCC和Microsoft Visual C++)中實現了。C++0x不是象牙塔中的科學研究,而是真實地來到了我們身邊。!
Danny Kalev: 總體而言,你認為將右值引用新增到C++0x是值得的嗎?除了效能的提升之外,一個典型的C++程式設計師還能從右值引用中獲得什麼其他的好處呢?比如更簡潔的設計,更簡單的演算法等等?
Bjarne Stroustrup: 我覺得將右值引用加入C++0x,不僅僅是值得,而是非常值得。移動語義可以作為一個長期存在的問題——如何從函式中返回一個體積較大的資料結構——的解決方案。對於這個問題,移動語義給了我們一個顯而易見的,簡單而高效的答案:直接將結果從函式中移動到目標位置;不需要複製結果;不需要在記憶體管理上玩什麼技巧;不需要使用混亂的特殊用途的記憶體管理方案;不需要函式的呼叫者預先申請記憶體;不需要通過額外的引數進行值的傳遞;不需要任何形式的垃圾回收機制。我認為這是右值引用的兩個應用中的最重要的一個。它將影響我們使用C++進行開發的每一個人,並且會讓我們的生活變得更好。開玩笑地說,以前很多人都說“聰明的程式設計師才能使用C++”,現在,有了移動語義,不那麼“聰明”的程式設計師也可以使用C++了。我們可以省掉我們的聰明瞭。
值得注意的是,寫一個有關右值引用移動的操作通常是一件非常簡單的事情,它不像送火箭上天那麼困難啦。
1 2 3 4 5 6 7 8 9 |
class Matrix { double* elem; // 指向成員變數的指標 int dim1, dim2; public: Matrix(Matrix&& a) :dim1(a.dim1), dim2(a.dim2), elem(a.elem) // 移動資料 { a.dim1=0; a.dim2=0; a.elem=nullptr; } // 將原來的資料清空 // …. }; |
這就是整個移動建構函式的完整過程:移動資料並將原來的資料清空。有了它的幫助,我們甚至可以簡單而高效地返回一個10000*10000的矩陣。
當然,對於程式庫的開發者而言這也將是非常重要的一天: 在程式庫中,有很多地方可以使用移動語義以簡化程式庫的實現,並且在更多的地方,轉發(右值引用的另外一個重要應用)將有助於程式庫的設計與實現。並且,移動語義和完美轉發並不是只有專家才能掌握的高深技術,每一個C++程式設計師都可以使用它們來簡化我們的程式,提高程式的效能。
Danny Kalev: 在一些C++0x新特性,諸如右值引用,Lambda表示式,的設計中有很多困難。一些批評者也聲稱,C++太老了並且不夠靈活。這些抱怨是否有一定的道理?會不會在將來的某一天,你決定不再擴充套件和改進C++,轉而使用一種新的程式語言代替?
Bjarne Stroustrup: 我更經常聽到抱怨是C++太靈活並且太大。新的語言往往是比較簡單的,因為它還沒有形成一個龐大的社群。所有語言都會隨著時間的流逝而增長。當然,修改一門大型的,有悠久歷史並已經被廣泛使用的程式語言要比推倒一切重來困難得多。但是,很多新語言都會夭折。並且,對於真實世界的應用來說,這些新語言顯得太過簡單了。向C++中新增新的內容是非常苦難的,對於一個新特性的建議者來說,要讓這個新特性獲得接受的過程通常也是漫長和痛苦的。但是,一旦這個新特性獲得接受,它將會對很多人產生十分重大的影響。如果我不想影響整個世界,我完全可以通過填字遊戲,寫小說或者是設計一門好玩的程式語言來讓自己的才智得到發揮。
當然,我也曾經夢想過設計一門比C++更新的、更小的、更好的程式語言,但是,每當我看到這門新語言可以解決的問題,以及這門新語言可能產生的影響,我就覺得大多數通過一門新的程式語言可以解決的問題同樣都可以通過改進C++及其標準庫來獲得解決。就對程式設計世界產生積極影響——至少對於我來說——而言,看起來比較繁瑣的對C++的不斷改進要比設計、實現和推廣一門新的程式語言要好得多。
Danny Kalev: 關於對Unicode的支援,C++0x提供了char_16和char_32,以及u16string和u32string來支援UTF16和UTF32編碼的字串。但是,它們在標準庫中的輸入輸出流中並沒有得到支援。例如,標準庫中沒有所謂的u16cout或u32cout。我想知道的是,我們該如何使用char16_t字串並將它們輸出?
Bjarne Stroustrup: 顯然,我們應該有支援unicode的輸入輸出流以及在標準庫中的其他的擴充套件對unicode進行支援。標準委員會知道這樣的需要,但是沒人有足夠的能力和時間來實現它。因此,不幸的是,這是C++中一個你不得不尋求第三方支援的地方。實際上有很多現有的程式庫都可以很好地支援unicode,例如,可以用於構建網路應用以及網際網路應用程式的Poco庫(http://pocoproject.org/index.html)。另外,Microsoft Visual C++對Unicode也有很好的支援。
不幸的是,我們並沒有從標準庫的層次上為unicode提供完整的支援,並且我們應該記住,大多數程式庫並應該也不能夠被包含在標準庫中。我的C++頁面上有很多關於程式庫,程式庫收集以及程式庫列表的連結,大約估計有超過10000個C++程式庫(包括商業的和開源的)。但是問題的關鍵是,你必須找到合適的程式庫並評估它們。
Danny Kalev: 最後,兔年已經到了,能不能和我們分享一下你的新年願望是什麼呢?
Bjarne Stroustrup:
▲讓C++0x成為一個正式的ISO標準
▲完成《C++程式語言》第四版的初稿
▲和我的外孫共度更多的美好時光
▲提出至少一個感興趣的新的技術觀點
原文來自:codeguru.com 翻譯:陳良喬