在 header 檔使用 constexpr 定義全域變數


這邊是看到《Quick Q: use of constexpr in header file》這篇文章,覺得還滿實用的,所以來記錄一下;他基本上是有人在 StackOverflow 上有人提出這個問題(頁面)後,相關的討論。

首先,「constexpr」這個關鍵字,是在 C++11 加入的東西(參考);它的用處,是將變數或函式宣告為可以在編譯階段(compile time)就可以計算出他的值。

透過這個關鍵字,除了可以讓編譯器在編譯時就針對這些東西最佳化外,更有可能讓寫出更多在編譯階段展開的語法。像是 C++17 的「if constexpr」(參考),也可以在撰寫某些程式時、更為方便。

而在某些地方,也可以看到有人建議用 constexpr 來取代 #define,來作為更好的變數定義(至少會是 type-safe 的)。

而這邊的問題是:

可以在 header 檔中,使用 constexpr 來定義變數嗎?當多個 .cpp 檔去 include 他的時候,會不會產生多個實體、或是造成變數重定義的問題?

這邊的答案是:

constexpr 基本上就會有 const 的效果,而全域、或在 namespace 裡的 const 則會有 static 的效果,進而讓每一個引用這個 header 檔的 .cpp 在編譯時,都產生一個內部、獨立的變數。

假設這邊在 header 檔裡面寫了下面的宣告

constexpr double PI=3.14;

那如果有兩個 .cpp 檔都有引用這個 header 檔,這兩個檔案裏面,會各自有一份獨立的「PI」這個變數。

如果在這兩個 .cpp 裡面,都執行

std::cout << "Value: " << PI << ", address: " << &PI 
<< std::endl;

這樣的程式,把值和其位址輸出的話,會得到類似下面的結果:

Value: 3.14, address: 01068B30
Value: 3.14, address: 01068B38

可以看到,雖然數值都是對的,但是記憶體位置會不相同。

而如果要不希望有多份變數的話,在 C++17 裡,則可以透過新的 inline variable 這個新的語法(參考),讓他不再是 static 的、進而不會被多次產生。

他的寫法就是:

inline constexpr double PI=3.14;

如此一來,在不同的 .cpp 裡面,也都會存取同一個變數,而不是各自的副本了。


而實際上,在 C++17 裡面,也可以透過 inline,來定義非 const 的全域變數!

比如說,如果在 header 裡面是寫

double PI=3.14;

的話,當有多個 .cpp 都 include 這個 header 檔的時候,就會出現 PI 這個變數被重新定義的錯誤。

而如果改成

inline double PI=3.14;

的話,就不會有這個問題了!


整體來說,這篇的重點其實是 C++17 的 inline variable 這種新語法。透過這樣的語法,以後要寫 header only 的函式庫,或是要在 header 裡面定義變數,就更簡單了~

對「在 header 檔使用 constexpr 定義全域變數」的想法

發表留言

這個網站採用 Akismet 服務減少垃圾留言。進一步了解 Akismet 如何處理網站訪客的留言資料