C++ 之父 2024 年末重磅演講 | 重新認識 C++:跨世紀的現代演進
作者 | 《新程序員》編輯部
出品 | CSDN(ID:CSDNnews)
12 月 5 日,美國國家工程院、ACM、IEEE 院士、C++ 之父 Bjarne Stroustrup 在「2024 全球 C++ 及系統軟件技術大會」上發表了題為《重新認識 C++:跨世紀的現代演進》的演講。屏幕上,演示文稿的第一頁就令人印象深刻:「C++ 幾乎可以實現我們所期望的一切!」
從構建操作系統到開發高性能遊戲引擎,從支持人工智能框架到驅動航天器控制系統,C++ 一直是系統級軟件開發的首選語言。然而,這位編程語言大師並不是在炫耀 C++ 的強大,而是要指出一個關鍵問題:「正因為它如此強大,我們更要謹慎選擇正確的使用方式。就像 goto 語句——它無所不能,所以我們幾乎從來不用它。同樣的,雖然用 20 世紀 80 年代的方式寫 C++ 也能完成任務,但這顯然不是最佳選擇。我們需要明確自己的真正需求,避免重蹈覆轍。」
Stroustrup 指出了一個常見的認知誤區:人們往往把「熟悉」等同於「簡單」。對很多開發者來說,見過千百遍的代碼寫法看起來簡單,而新的特性和方法則顯得複雜。
「我們必須努力避免這種思維定式,否則就會永遠停留在 20 世紀。」他強調道,「今天,我想談談我所認為的當代 C++、現代 C++ 的基礎是什麼。我認為,當代的編程方式能讓代碼變得更簡單、更安全、更高效,遠勝於任何舊版本的 C++。(在這一語境下,「當代」往往指的是 C++20/23/26 等當前的版本)」
當代 C++ 的簡潔之美
為了說明當代 C++的優勢,Stroustrup 首先以他收到的一個來自《龍書》(Dragon Book,編譯器設計領域的經典教材)作者、AWK 語言的創造者之一 Alfred V. Aho 的難題為例。這個例子展示了如何用 C++ 簡潔地處理文本中的不重覆行:
import std;
using namespace std;
int main() // 輸出輸入流中的不重覆行
{
unordered_map<string,int> m; // 哈希表
for (string line; getline(cin,line); )
if (m[line]++ == 0)
cout << line << '\n';
}
「這段代碼展示了幾個重要特點,」Stroustrup 解釋道,「首先,完全沒有使用預處理器;其次,代碼高效且容易理解;第三,如果需要進一步優化,也完全可以做到——但關鍵是,在開始優化之前,這段代碼本身就已經相當高效了。」
「讓我們試試另一種處理不重覆行的方式。」他進一步提出,「為什麼要一直輸出行呢?也許我想要的是一個僅僅收集輸入中不重覆行的程序。」
這樣一個簡單函數就能輕鬆實現:
vector<string> collect_lines(istream& is) // 從輸入中獲取不重覆行
{
unordered_set<string> m; // 哈希表
for (string line; getline(is,line); )
m.insert(line);
return vector(m.begin(),m.end());
}
auto lines = collect_lines(cin);
「C++ 的類型系統會自動推導出我們需要的是 string 的 vector,」Stroustrup 解釋說,「而且返回時不需要複製,直接移動就行了。這樣的實現既簡潔又高效。」
「但這裏的 vector 構造有點囉嗦。我希望 vector 能直接接受這個集合本身,」Stroustrup 說,「所以我寫了一個函數,它可以接受任何範圍並從中創建 vector。」於是他展示了一個更簡潔的版本:
vector<string> collect_lines(istream& is) // 從輸入中獲取不重覆行
{
unordered_set<string> m; // 哈希表
for (string line; getline(is,line); )
m.insert(line);
return make_vector(m);
}
auto lines = collect_lines(cin);
「標準庫不需要提供所有功能。有時候自己寫個簡單函數就能解決問題,比如這個 make_vector。」他最終總結道,「也許在 C++ 的未來版本中,vector 會直接支持這種構造方式,那時這個函數就不需要了。」
「從這些例子可以看出我最重視的一點:思想的直接表達。」Stroustrup 緊接著列出了他重視的每一項事物:
談到 C++ 的發展歷程,Stroustrup 指出:「一些關鍵特性和技術已有多年歷史,比如帶構造函數和析構函數的類、異常處理機制、模板、std::vector……等等。另一些則是較新的發展,如 constexpr 函數和 consteval 函數、lambda 表達式、模塊、概念、std::shared_ptr……等等。關鍵在於將這些特性作為一個整體來運用。」
「不要盲目使用所有新特性,也不要局限於僅使用新特性,」他強調道,「如果想瞭解最新特性和未來發展方向的更多細節,可以參考相關的技術討論影片。我更關注的是如何將語言作為一個整體來開發好的軟件。因為最終編程語言的價值體現在其應用程序的質量之中。」
資源管理:C++ 的基石
「我們知道,相比歸還東西,人們更傾向於獲取東西,」Stroustrup 首先打了個生動的比方,「問任何一個圖書管理員就知道了,人們借書後常常忘記還書。在大型軟件中,如果我們必須顯式地返還借用的資源,我們肯定會遺漏一些。」
Stroustrup 將資源定義為「任何必須獲取並在之後釋放(歸還)的對象」。「這包括內存、string(字符串)、互斥鎖、文件句柄、套接字、線程句柄、著色器等等很多東西,」他解釋道。「從這個詞的含義來看,在編程中我們要處理的很多東西都是資源。」
在 C++ 中,每個資源(resource)都應該有對應的句柄(handle)來管理它的生存期。句柄負責資源的訪問和釋放,這種機制是通過對生存期的嚴格控制來實現的。
為瞭解決這個問題,Stroustrup 提出了幾個關鍵原則:
1. 避免手動釋放資源——不要在應用程序代碼中出現 free()、delete 等資源釋放操作;
2. 使用資源句柄——每個對象都由負責訪問和釋放的句柄管理;
3. 基於作用域管理——所有資源句柄都屬於特定作用域,可以在作用域間轉移;
他用一段簡單但富有啟發性的代碼來說明這些原則:
template<typename T>
class Vector { // T 類型元素的 vector
public:
Vector(initializer_list<T>); // 構造函數:分配內存並初始化元素
~Vector(); // 析構函數:銷毀元素並釋放內存
// ...
private:
T* elem; // 指向元素的指針
int sz; // 元素數量
};
void fct()
{
Vector<double> constants {1, 1.618, 3.14, 2.99e8};
Vector<string> designers {"Strachey", "Richards", "Ritchie"};
// ...
Vector<pair<string,jthread>> vp { {"producer",prod}, {"consumer",cons}};
}
「這就是 C++ 的基石:構造函數(constructor)和析構函數(destructor),」Stroustrup 說道,「如果需要獲取任何資源,那是構造函數的工作;如果需要歸還資源,那是析構函數的工作。這裏我們將抽像層次從機器級的指針和大小提升到了更高的層次。我們把它包裝成一個類型,這個類型行為正確,有賦值操作,有訪問函數,並且能正確清理。」
他特別指出了資源管理機制的遞歸性:「string 擁有一些字符,這裏的 pair 擁有一個 string 和一個 jthread。jthread 擁有對操作系統線程的引用。這些都是遞歸進行的。神奇之處在於最後的閉合花括號——那裡是所有東西都被隱式而可靠地清理的地方。」
為了做好資源管理,Stroustrup 強調了對生存期的控制:
-
構造:首次使用前建立對象的不變量(如果有的話);
-
析構:最後使用後釋放所有資源(如果有的話);
-
複製:a = b 意味著 a == b,且它們是獨立的對象;
-
移動:在作用域間轉移資源擁有權;
「這些機制讓我們能夠開發出更安全、更可靠的代碼,」他總結道,「因為資源管理不再依賴於程序員的記憶力,而是由語言機制自動保證。」
錯誤處理的策略
「在確保資源安全的基礎上,我們還需要有明確的錯誤處理(error handling)策略,」Stroustrup 隨即轉入了另一個重要話題。他指出,C++ 中有兩種主要的錯誤處理方式,它們各有適用場景:
「對於那些常見且可在局部處理的失敗情況,使用錯誤碼(error code)是合適的,這種方式避免了使用效率低下且醜陋的 try-catch 結構。」他解釋了第一種情況,「但問題是,我們經常忘記檢查錯誤碼,這可能導致錯誤的結果繼續傳播。而且,這種方式不適用於構造函數和運算符。比如說,當你寫 Matrix x = y + z 這樣的表達式時,就沒有地方放置錯誤返回語句和測試。」
「另一方面,對於那些罕見且無法在局部處理的錯誤,異常處理(exception handling)是更好的選擇。」Stroustrup 繼續說道,「錯誤可以沿調用鏈向上傳播,避免陷入 ‘錯誤碼地獄’。未捕獲的異常會導致程序終止,而不是產生錯誤結果。重要的是,這種機制必須與 RAII(資源獲取即初始化)配合使用,依賴作用域資源句柄。」
他用一個具體的例子說明了這個觀點:
void fct(jthread& prod, string name)
{
ifstream in { name };
if (!in) { /* ... */ } // 預期可能發生錯誤
vector<double> constants {1, 1.618, 3.14, 2.99e8}; // 內存可能耗盡
vector<string> designers {"Strachey", "Richards", "Ritchie"}; // 嵌套構造
jthread cons { receiver };
pair<string,jthread&> pipeline[] { {"producer", prod}, {"consumer", cons}};
// ...
}
「想像一下,如果只使用單一的錯誤處理方式,這段代碼會變得多麼複雜,」Stroustrup 說,「每個操作都可能失敗:文件打開可能失敗,內存分配可能失敗,構造過程可能失敗。使用異常處理,我們可以集中處理這些錯誤,而不是在每個可能的失敗點都編寫檢查代碼。」
Stroustrup 還提到了一個最新的研究發現:「即便對小型系統,異常處理也可能比錯誤碼更高效。我們最近看到一個很好的演示,展示了在小型固件中使用 C++ 異常可以產生更小、更快的代碼。」
「關鍵是要記住,」他強調,「錯誤處理不是要選擇唯一正確的方式,而是要根據具體情況選擇最合適的方式。有時是錯誤碼,有時是異常,重要的是要有一個明確的策略。即便對小型系統,異常處理機制也可能比錯誤碼更高效。Khalil Estell 最近在 CppCon 2024 上的演示*展示了在小型固件中使用 C++ 異常可以產生更小、更快的代碼。」
* Khalil 的演講內容:https://www.youtube.com/watch?v=bY2FlayomlE
模塊:打破「包含」的魔咒
談到代碼組織,Stroustrup 首先指出了一個困擾 C++ 開發者多年的問題:「頭文件包含的順序依賴問題一直是個麻煩。#include “a.h” 後跟 #include “b.h”,可能與順序顛倒後的結果完全不同。這種基於文本的包含機制會導致:包含具有傳遞性、相同的代碼被重覆編譯多次、容易引發宏定義衝突等問題。」
相比之下,C++20 引入的模塊(modules)機制則完全不同:
import a;
import b;
「這與順序無關,」Stroustrup 解釋道,「寫成下面這樣,效果完全一樣。」
import b;
import a;
「import 不具有傳遞性,模塊化的代碼更加乾淨,而且能顯著提升編譯速度——這不是百分比級的提升,而是數量級的提升。」
緊接著,他興奮地宣佈:「經過幾十年,我們終於在 C++ 中實現了模塊。我們不必再使用 include 了!這是我長期計劃的一部分——逐步淘汰 C 預處理器。預處理器會給工具帶來麻煩,因為工具看到的和程序員看到的是不一樣的。」
他分享了來自一家德國嵌入式系統公司的實際案例。該公司有一個傳統的設備信息庫 libgalil,通過頭文件包含機制,最終會展開成約 50 萬行代碼(其中 15 萬行是空行)。即便以現代編譯器的速度,處理這樣的代碼也需要 1.5 秒。然而,當他們將其改造為模塊後,預處理後只有 5 行代碼,編譯僅用了 62 毫秒,實現了 25 倍的速度提升。
「當然,你不能期待在所有情況下都能獲得 25 倍的提升,」Stroustrup 說,「但根據經驗,使用具名模塊通常能讓編譯速度提高 7-10 倍。這就是促使人們將代碼從舊風格改造為新風格的動力。雖然這個過程並不容易——畢竟我們有數十億行現存的代碼——但這種改進確實顯著。」
在標準庫方面,C++23 已經提供了模塊化支持。最重要的是模塊 std,它包含了完整的 std 命名空間:
import std; // 包含整個標準庫
「這帶來了驚人的改進,」Stroustrup 說,「以十分之一的時間提供十倍的信息——效率提升了 100 倍。這意味著我不用像以前那樣等待編譯時喝那麼多咖啡了。」
他特別推薦觀看 Danielle Eckert 在 2022 年 CppCon 上題為《Contemporary C++ in Action》的演講,「這個演講展示了現代 C++ 特性在實際項目中的應用,非常精彩。如果可能,你一定要去看一下這個影片。」
影片鏈接:https://www.youtube.com/watch?v=yUIFdL3D0Vk
「模塊化的概念早在 1994 年的《C++ 語言的設計和演化》中就已提出,」Stroustrup 補充道,「現在我們終於實現了!這個特性不僅讓代碼更清晰,也大大提升了開發效率。」
泛型編程與概念
「泛型編程(generic programming)是當代 C++ 的關鍵基礎,」Stroustrup 如此介紹道,「這個想法最早可以追溯到 80 年代初。那時我就描述過這個概念,只是當時我以為可以用宏來實現——關於這點我錯了,但對需要泛型編程這一點我是對的。現代 C++ 中的大量泛型編程思想都來自 Alex Stepanov。」
泛型編程為 C++ 帶來了多方面的優勢:代碼更加簡潔、思想表達更直觀、實現零開銷抽像、保證類型安全。它在標準庫中無處不在:容器和算法、併發、內存管理、I/O、string 和正則表達式等。
Stroustrup 用一個簡單的例子說明了基於概念的泛型編程:
void sort(Sortable_range auto& r);
vector<string> vs;
// ... 填充 vs ...
sort(vs);
array<int,128> ai;
// ... 填充 ai ...
sort(ai);
list<int> lsti;
// ... 填充 lsti ...
sort(lsti); // 編譯錯誤:list 不支持隨機訪問
「這段代碼展示了幾個隱含的要求,」他解釋道,「容器的類型、元素的類型、元素的數量、比較準則等。概念(concept)的作用就是明確指定對類型 r 的要求。當編譯器看到 vector 時,它會問:‘這個 vector 有支持隨機訪問的元素序列嗎? ’ 是的,有。 ‘這些元素是可以比較的嗎?’ 是的。所以代碼可以工作。但對於 list,因為它不支持隨機訪問,編譯器會立即發現這個錯誤。」
Stroustrup 特別指出,「概念不等同於 ‘類型的類型’。概念是可以作用於多個類型的謂詞。有些概念還可以接受值參數,可以混合類型和值。但概念本質上是函數,而不是像其他語言中那樣僅僅是函數簽名的集合。」
隨後,他進一步解釋了為什麼 list 不支持隨機訪問是一個特意的設計:「如果在一個包含 50 萬個元素的 list 中使用下標訪問,會非常非常慢。所以這種限制實際上是在保護開發者避免寫出性能糟糕的代碼。」
更複雜的情況是,許多算法都需要多個模板參數類型,而且這些類型之間往往需要建立某種關係。Stroustrup 展示了一個來自標準庫的例子:
template<ranges::input_range R,
indirect_unary_predicate<iterator_t<R>> Pred>
Iterator_t<R> ranges::find_if(R&& r, Pred p);
vector<string> numbers; // 存儲數字的 string,如 "13" 和 "123.45"
// ... 填充 numbers ...
auto q = find_if(numbers,
[](const string& s) { return stoi(s) < 42; }); // lambda 表達式
「這裏的概念實際上是編譯期謂詞(compile-time predicate),它們在編譯期執行並返回布爾值。通常一個概念會基於其他概念構建,形成一個完整的類型約束體系。」
Stroustrup 表示,概念並不是什麼新鮮事物:「每個成功的泛型庫都包含某種形式的概念。它們存在於設計者的構思中,記錄於技術文檔中,體現在代碼註釋中。比如 C/C++ 的內置類型概念(算術類型和浮點類型)、STL 中的迭代器、序列和容器概念、數學概念(單子、群、環、域)、圖論概念(邊、頂點、圖、有向無環圖等)……」
講到此處,Stroustrup 致敬了 C 語言之父丹尼斯·里奇(Dennis Ritchie, 1941-2011):
「實際上,我們只是為這些概念提供了語言層面的表示方法,讓編譯器能夠理解和檢查它們。這要求我們學會有效運用這種語言支持。」他說道,「編譯期編程(compile-time programming)在當今的 C++ 中非常基礎,這種技術也已經存在很長時間了。有趣的是,在 2010 年時,還有人斷言編譯時求值不僅毫無用處,而且根本無法實現。現在的發展已經證明,他們的判斷完全錯了。」
「使用概念還帶來了許多實際好處:支持更好的程序設計,提升代碼可讀性和可維護性,避免過度使用無約束的 auto 和 typename,大幅改進錯誤信息。」他打了個比方:「還記得那個只有內置類型沒有類(class)的年代嗎?C++ 從未有過這種時期,但現在的 C 語言仍是這樣。好在 C 現在也有了函數原型。」
「概念的引入還極大地簡化了條件約束的表達。」Stroustrup 展示了一個例子:
template<typename T> class Ptr {
// ...
T* operator->() requires is_class<T>; // 僅當 T 是類時才提供 -> 運算符
};
template<typename T, typename U> class Pair {
// ...
template<convertible<T> 湯臣, convertible<U> UU>
Pair(const 湯臣&, const UU&); // 只為可轉換為成員的類型提供構造函數
};
「傳統的 enable_if 方案原始、醜陋、非通用,且容易出錯,」他最終評價道,「而使用概念,我們可以用更簡潔、更直觀的方式表達這些約束。」
協程:狀態保持!
「說到協程(coroutine),這其實是個有趣的故事,」Stroustrup 回憶道,「在 C++ 發展的最初十年,協程是我們的一個重要優勢。但後來一些公司因為它不適合他們的機器架構而反對,結果我們失去了這個特性。現在,我們終於把它找回來了。」
協程的特點是能在多次調用之間保持其狀態。Stroustrup 用一個生成斐波那契數列的例子來說明:
generator<int> fibonacci() // 生成 0,1,1,2,3,5,8,13 ...
{
int a = 0; // 初始值
int b = 1;
while (true) {
int next = a + b;
co_yield a; // 返回當前斐波那契數
a = b; // 更新狀態
b = next;
}
}
for (auto v: fibonacci())
cout << v << '\n';
「這裏的妙處在於狀態的保持,」他解釋道,「我們有計算的狀態——a、b 和 next——它就這樣不斷地計算下一個斐波那契數。協程已經被嵌入到迭代器系統中,所以我們可以用簡單的 for 循環來獲取數列中的值。」
但這個例子還有一個小問題:「這是個無限序列,顯然會帶來問題,我們會遇到溢出。那麼如何限制它只生成特定數量的值呢?」Stroustrup 展示了改進的版本:
template<int N>
generator<int> fibonacci() // 生成前 N 個斐波那契數
{
int a = 0;
int b = 1;
int count = 0;
while (count < N) {
int next = a + b;
co_yield a;
a = b;
b = next;
count++;
}
}
for (auto v: fibonacci<10>()) // 只生成十個數:0,1,1,2,3,5,8,13,21,34
cout << v << '\n';
「雖然標準庫對協程的支持還不如我想要的那麼完善,」Stroustrup 說,「但這個例子中使用的 generator 已經在 C++23 中可用了。如果你使用的是較早的編譯器,還可以使用 Facebook 的 coro 庫或其他任務庫。這個例子很好地展示了模板和協程是如何和諧地協同工作的。」
「協程為我們提供了一種漂亮的方式來處理需要保持狀態的計算,」他總結道。「它讓代碼更容易理解,也更容易維護。這正是我們一直追求的目標:簡單的事情簡單做。」
調優:「洋蔥原則」
「對某些代碼來說,調優是必要的,」Stroustrup 轉入了性能優化的話題。「但我們都聽過 ‘避免過早優化’ 這個建議。重要的是要在優化前後都進行性能測量,同時在設計接口時就要考慮優化空間。」
他提出了幾個關鍵原則:
1. 接口設計需明確定義
2. 保持類型信息的完整性
3. 提供足夠信息支持檢查和優化
4. 管理複雜度:”簡單的事情簡單做!”
「我把這個叫做 ‘洋蔥原則’,」Stroustrup 打了個生動的比方,「你可以把代碼想像成洋蔥的層。每當我們需要優化或處理特殊情況,我們就可能需要剝掉一層抽像。但要記住,每剝掉一層,你就會哭得更厲害。」
「為什麼會這樣?」他繼續解釋道,「因為每深入一層,你就有可能遇到更多的錯誤,必須寫更多的代碼,代碼也更難理解。所以在真正需要之前,不要輕易剝掉一層抽像。這就是我對 ‘不要過早優化’ 的理解。」
此外,關於併發(concurrency),Stroustrup 指出這是一個需要單獨討論的重要話題。「你需要它來提高效率。在標準庫中有廣泛的、相當低層的併發支持:線程和鎖、共享機制、並行算法、協作取消、Future 機制、協程等等。這些都是為了性能,但同時也帶來了複雜性。」
指南和規格配置:走向未來
「不要停留在 20 世紀,」演講走進尾聲時,Stroustrup 直截了當地說,「但這說起來容易做起來難。大多數代碼都包含一些舊的部分,升級這些代碼既困難又耗時。雖然升級能帶來巨大好處——比如引入模塊能顯著提升編譯速度——但要摒棄次優技術確實很難,因為我們不僅要面對海量的歷史代碼,還要克服根深蒂固的編程習慣。」
「我們面臨一個根本性的問題,我們不能改變語言本身——穩定性和兼容性是 C++ 的核心優勢。但我們可以改變使用語言的方式。」
為了說明這一點,Stroustrup 分享了一個有趣的觀察:「大約每兩週,我都會收到一個有趣的請求。這個請求總是包含三個部分:首先是跟我說 ‘C++ 太複雜了,必須簡化’——我同意,確實如此——但緊接著又跟我說 ‘順便提一下,我們絕對需要增加這兩個新特性’。而且還要保證 ‘別破壞我的代碼,我可是有上百萬行呢’。這三件事是沒法同時做到的。」
「正是基於這種現實,」他繼續說,「我們採取了一種務實的策略:通過一套靈活的指南規則體系來簡化語言的使用,而不是改變語言本身。這些指南可以根據項目需求選擇性採納。比如 C++ Core Guidelines 就是一個很好的例子,而且已經有了實踐工具的支持,如 Visual Studio、GCC 和 Clang-Tidy 都能幫助執行這些指南——這不是科幻小說,這些工具現在就可以使用。」
在標準委員會中,Stroustrup 和同事們正在推進一個更進一步的方案:規格配置(profile)。「每個規格配置是一套強製性的指南規則,」他解釋道,「雖然現在還在製定中,但其目標很明確:讓開發者能夠根據需要選擇不同類型的安全性級別和執行強度。這將幫助我們在保持語言強大的同時,使其更容易正確使用。」
Stroustrup 建議的初始規格配置包括:
1. 算法:全面的範圍檢查,禁止解引用 end() 迭代器;
2. 算術:檢測上溢和下溢;
3. 類型轉換:全部禁用;
4. 併發:消除死鎖和數據競爭(這是個難點);
5. 初始化:所有對象必須初始化;
6. 失效:禁止通過已失效的指針訪問(包括懸空指針);
7. 指針:禁止對內置指針使用下標操作(應使用 span、vector、string 等);
8. 範圍:捕獲範圍錯誤;
9. RAII:所有資源必須由句柄管理;
10. 類型:涵蓋初始化、範圍、轉換、失效和指針規則;
11. 聯合體:禁止使用 union(應使用 variant 等);
他說:「我們需要那些底層的、複雜的、接近硬件的、容易出錯的、專家級的特性,因為它們是高效實現高層功能的基礎。很多底層特性在正確使用時都很有價值。但一旦我們有了這些基礎,就可以在此之上建立更安全、更簡單的編程模型。」
「我們想要的是「增強版 C++」——簡單、安全、靈活、高效,而不是功能受限的子集。我們不能失去 C++ 最重要的特性:高性能和對硬件的直接控制。而且這些改進不會改變語言的本質,最終的代碼仍然是符合 ISO C++ 標準的。」
Stroustrup 還總結了 C++ 的編程模型:
-
靜態類型系統,同時支持內置類型和用戶定義類型
-
支持值語義和引用語義
-
統一的資源管理機制(RAII)
-
高效的面向對象編程
-
靈活且高效的泛型編程
-
編譯期編程
-
直接訪問硬件和操作系統
-
通過庫實現的併發支持(借助內部指令)
-
最終淘汰 C 預處理器
在演講的最後,Stroustrup 展示了一張 C++ 用戶數量增長的圖表。
「C++ 在設計之初就考慮到語言會不斷演進,」他說,「從 1979 年的 C with Classes,到 1998 年引入異常、模板和命名空間,再到 2011 年增加併發、lambda 表達式和智能指針,直到 2020 年帶來概念、協程、模塊等特性,C++ 一直在成長。」
「我從一開始就知道,我不可能獨自在合理的時間內構建出我想要的完整語言,」他坦言道。「所以 C++ 在不斷演進,現在有了很多真正有用的新特性。但關鍵是,我們要活在曲線的上方,使用當代 C++ 中可用的工具,而不是停留在最古老的部分。因為歸根到底——編程語言的價值體現在其應用程序的質量之中。」
在 C++ 之父 Bjarne Stroustrup 的主題演講《重新認識 C++:跨世紀的現代演進》中,他系統地探討了資源管理、錯誤處理、模塊化、泛型編程等 C++ 的核心特性,並詳細闡述了他對 C++ 未來發展的願景。而在同場的爐邊談話環節中,Bjarne Stroustrup 不僅分享了對當前 C++ 發展現狀的思考,也表達了對未來的期待。
想要第一時間深入瞭解 C++ 之父的觀點,或重溫他對 C++ 技術演進的精彩論述?為了幫助開發者更好地理解和運用現代 C++,CSDN 聯合 Boolan 特別策劃了「C++ 名家系列直播活動」。全球頂尖的 C++ 專家,將為參與者深入解析現代 C++ 的核心理念和實踐經驗。更重要的是,我們還將向參與者獨家贈送《現代 C++ 白皮書》中文版。這份白皮書凝聚了 C++ 之父 Stroustrup 15 年來的技術思考,以及全球 C++ 專家群體的智慧結晶,堪稱現代 C++ 開發者的必讀指南。