幾週前,有讀者詢問為何無法在 C++/CLI 中宣告 const 成員函數。乍看之下,這似乎只是個語法限制,但背後卻隱藏著 C++/CLI 設計的核心哲學。CLI (Common Language Infrastructure) 為了支援 Visual Basic、Java 甚至 COBOL 等多種語言,並不理解 const 的語義,因此無法在元數據中直接表達 const 成員函數。不過,C++/CLI 仍允許 const 資料成員與參數,透過 modopt (選擇性修飾符) 標記,讓懂得的語言能利用,不懂的語言可忽略。
Herb Sutter 撰寫的《A Design Rationale for C++/CLI》詳細闡述了設計決策背後的理由。核心目標只有一個:讓 C++ 成為一等 CLI 公民。.NET Framework 是 Windows 開發的未來,連 COBOL 都支援 CLI,C++ 豈能缺席?
但為何不直接用庫或模板實現?Bjarne Stroustrup 指出:「只有庫的道路確實不可行時,才應走語言擴展之路。」CLI 引入了屬性、垃圾回收等新概念,需要特殊的元數據與代碼生成,純庫方式無法辦到。例如屬性需要特殊元數據,無法用模板表達。
舊版 Managed Extensions 為托管與原生物件同用 *,試圖統一兩者,卻掩蓋了關鍵差異:解構語義、複製建構函數、虛擬呼叫行為皆不同。C++/CLI 採用 ^ (handle) 與 % (tracking reference),隱藏表面差異,凸顯本質差異。這也帶來意外紅利:未來原生類別可從托管堆配置,反之亦然。
關鍵字去底線化 (__gc → ref) 也是基於 Bjarne 理念:「重要操作應高度可見」,且位置關鍵字不會與既有代碼衝突。
C++/CLI 允許將參考類別 R r; 當作區域變量,編譯器自動生成 gcnew、try/finally 與 delete,確保解構函數在離開作用域時確定性執行。這恢復了 C++ 程式員熟悉的 RAII 模式,解決了 Managed Extensions 非確定性解構的痛點。
「基本原理」揭示了未來規劃:原生與托管類別可互相繼承,透過非連續記憶體佈局實現。Bjarne 早年堅持「物件不必連續排列」,為今日混合物件埋下伏筆。內部代號 MC^2 (Managed C++ squared) 致敬愛因斯坦,但最終選 C++/CLI,強調 C++ 在前,避免「修飾的 C++」感覺。
從撥動開關、紙帶、FORTRAN/C、C++、COM/DLL,到 .NET CLI 插入虛擬機層,實現語言互通、安全性、垃圾回收。Windows Vista/.NET 3.0 將系統重寫為 CLI 類別,可重用、語言中立的組件目標終於達成。C++/CLI 確保 C++ 不被邊緣化,讓熱愛 C++ 的開發者能在新紀元繼續使用它。
這是「工作中的 C++」專欄第 165 篇、跨越 14 年的最後一篇。作者 Paul DiLascia 以幽默自比 Cal Ripken (鐵人連續出賽紀錄保持者),感謝讀者、編輯同事與早期主編 Gretchen Bilson。專欄雖退休,但對 C++ 的承諾不變,未來仍會在 MSDN 發文。
「編程確實變容易了,而 C++/CLI 正是通往這個勇敢新世界的橋樑。」
圖 2:從物理機器到虛擬機的範式轉換,以及 C++/CLI 的定位