[106] const 變數有助理解程式碼並協助編譯器優化

前一篇討論函數撰寫風格,這一篇談 const 區域變數(local variable)的使用習慣。先把範例碼列出來:

int CalcLineCountOfLabel(const Label& label)
{
	const auto label_total_width = label.getFont().getStringWidth(label.getText());
	const auto bound_width = label.getLocalBounds().getWidth();

	int line_count = 1;
	if (label_total_width > bound_width)
		line_count += label_total_width / bound_width;

	return line_count;
}

該函數中的二個區域變數 label_total_widthbound_width 用來儲存呼叫外部函數的結果。我把該些變數宣告為 const 明確表示初始化後其值不會再變。看過不少程式碼,發現許多人不會特別把這種不會再變的變數宣告成 const。兩種寫法表面上沒有不同,卻存在著關鍵的差異。

初始化後其值不會再變的區域變數(本文針對的是變數,而不是常數)明確宣告成 const 至少有兩個好處:

  1. 幫編譯器優化程式碼
  2. 提高程式碼可讀性

幫編譯器優化程式碼

當代 C++ 編譯器個個聰明有幹勁,命令給出去,她們會用盡所能幫你優化程式碼。話雖如此,面對語意模稜兩可的程式碼,編譯器還是有力不從心的時候。

專業的程式設計師必須在不減損生產力的情況下,「協助」編譯器優化程式碼,作法是撰寫意圖明確的程式碼。讓我們來比較有 const 跟沒有 const 的程式碼,其編譯後的 Assembly 差異。

底下使用免費線上工具 Compiler Explorer,編譯器選的是 clang 3.8:

左邊是沒有 const 的版本;② 右邊是把兩個區域變數加上 const 的版本

可以看到編譯出的 Assembly 最大的差異為 ① 含有兩個 add 運算,而 ② 則沒有。而且許多運算結果在編譯時期即產生,此範例效能提昇有限,但可以看出 const 對編譯器優化程式碼的影響。

在 StackOverflow 上找到 const 優化的提問,其中一個回答是這樣:

Compiler can optimize away this const by not providing storage to this variable rather add it in symbol table. So, subsequent read just need indirection into the symbol table rather than instructions to fetch value from memory.

提高程式碼可讀性

比起程式碼優化,程式碼可讀性更為重要。

const 讓變數的值變得容易「預測」,進而讓程式碼較易理解。把複雜的運算結果存到變數並令其值固定不再變動,再加上貼切的變數名稱,雙管齊下,對於理解大型函數很有幫助。

檢視大型函數或複雜程式區塊時,若發現適合宣告成 const 的變數,我會動手加上 const,簡單而且對專案有益無害的改動,有時還因此發現「重構點」,好處不少咧。

延伸閱讀

GotW #81 討論的是函數參數與回傳值為 const,與本文的範圍不同。有興趣的朋友可再深入研究。🔚