這標題,真的很clickbait..... 這份筆記會針對constexpr與編譯期初始化的關聯做說明
- constexpr,一般來說會用在標示某個變數需要於編譯期(compile time)就完成初始化
1 | int get_val(int i) { |
- 讓我們來看一看這個很簡單的範例出來的asm(x86_64 gcc -O0)
1 | get_val(int): |
基本上就是很老實地把數值算出來,但是如果啟用最佳化又會是什麼情況(x86_64 gcc -O3)
1
2
3
4
5
6get_val(int):
lea eax, [rdi+rdi]
ret
main:
mov eax, 4
ret數值直接在編譯的時候算出來,寫進code了
在影片裡頭提到constexpr到底會不會被編譯期初始化這一點永遠是不確定的,他會受到最佳化等級還有編譯器本身的影響
- 我自己的經驗是,多半都會,唯一你必須要確保你有加上constexpr
此範例中的value,無論最佳化等級開多少,都會是編譯期求解
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
constexpr std::array<int, 100> get_val() {
std::array<int, 100> result{};
for (int n = 0 ; n < result.size() ; ++n) {
result[ n ] = n ;
} // for
return result;
}
int main() {
constexpr auto value = get_val()[45] ;
return value ;
}
- 我自己的經驗是,多半都會,唯一你必須要確保你有加上constexpr
我們現在要來進一步探討constexpr variable的storage duration,我把影片中的例子人肉OCR出來了
- here
- 看似沒什麼問題的程式碼,居然觸發了ASAN,到底是怎麼回事?
- 我們前面提到constexpr會讓變數在編譯期初始化但我們仔細看一下asm(in
main(),為了簡潔我把ASAN關掉了)
1
2
3
4lea rdi, [rbp - 4016]
movabs rsi, offset .L__const.main.values
mov edx, 4000
call memcpy@PLT - 驚人的事實是,array本身的確是constexpr,但是我們用了另外一個指標p去取他的值
- 然後還在scope裏頭,這下可好,p會指向我們從executable搬到stack上的array
- 你是不是發現了,即便宣告成constexpr,但她的storage duration依然不是static,這個constexpr的array居然是automatic(我指它的storage duration)
- 這個程式在-O3的時候,最佳化直接編譯求值了,所以不會被ASAN抓到
- 但是在-O0的時候我們有去做搬移的動作,然後就華麗的爆炸
所以結論是,我們原本所想的constexpr跟實際上的constexpr是有差異的
- 最接近我們所想的應該是static
constexpr,我們把先前的範例改一改
- 在constexpr前加上static,你就會看到剛剛提到的那個搬移動作不見了
- 因為現在values的storage duration是static
- 我的老天鵝阿
- 最接近我們所想的應該是static
constexpr,我們把先前的範例改一改