TLDR
修飾變量的時(shí)候,可以把 constexpr 對(duì)象當(dāng)作加強(qiáng)版的 const 對(duì)象:const 對(duì)象表明值不會(huì)改變,但不一定能夠在編譯期取得結(jié)果;constexpr 對(duì)象不僅值不會(huì)改變,而且保證能夠在編譯期取得結(jié)果。如果一個(gè) const 變量能夠在編譯期求值,將其改為 constexpr 能夠讓代碼更清晰易讀。
constexpr 函數(shù)可以把運(yùn)行期計(jì)算遷移至編譯期,使得程序運(yùn)行更快(但會(huì)增加編譯時(shí)間)。但如果 constexpr 函數(shù)中存在無(wú)法在編譯期求值的參數(shù),則 constexpr 函數(shù)和普通一樣在運(yùn)行時(shí)求值,此時(shí)的返回值不是常量表達(dá)式。
1. 常量表達(dá)式和 constexpr
C++11 中引入了 constexpr 關(guān)鍵字。constexpr 是 const expression 的縮寫(xiě),即常量表達(dá)式。
常量表達(dá)式是指值不會(huì)改變且編譯期可以得到結(jié)果的表達(dá)式。
1.1 特點(diǎn)
值不會(huì)改變(這一點(diǎn)和普通 const 一樣)
編譯期就能得到結(jié)果?。ㄆ胀?const 不一定保證)
1.2 使用場(chǎng)景
C++ 在一些場(chǎng)景下必須使用常量表達(dá)式,比如:
數(shù)組大小
整型模板實(shí)參(如 std::array<T, N> 的長(zhǎng)度參數(shù) N)
switch-case 中的 case 標(biāo)簽
枚舉量的值
對(duì)齊規(guī)格
1.3 常見(jiàn)的常量表達(dá)式
字面值(如 42)
用常量表達(dá)式初始化的 const 對(duì)象
一個(gè)對(duì)象(或表達(dá)式)是否是常量表達(dá)式取決于類型和初始值,如:
int i1 = 42;? ? ? ? ? // i1 不是常量表達(dá)式:初始值 42 是字面值,但 i1 不是 const 類型
const int i2 = i1;? ? // i2 不是常量表達(dá)式:初始值 i1 不是常量表達(dá)式
const int i3 = 42;? ? // i3 是常量表達(dá)式:用字面值 42 初始化的 const 對(duì)象
const int i4 = i3 + 1; // i4 是常量表達(dá)式:用常量表達(dá)式 i3 + 1 初始化的 const 對(duì)象
const int i5 = getValue(); // 如果 getValue() 是普通函數(shù),則 i5 值要到運(yùn)行時(shí)才能確定,則不是常量表達(dá)式
1.4 constexpr 變量
上面的例子可以看出,不能直接判斷一個(gè) const 對(duì)象是否是常量表達(dá)式:例如 i4 是否是常量表達(dá)式取決于 i3 是否是常量表達(dá)式,而 i4 又可能用來(lái)初始化其他常量表達(dá)式。在復(fù)雜的系統(tǒng)中,很難一眼看出某個(gè) const 對(duì)象是否是常量表達(dá)式。
C++11 允許把變量聲明為 constexpr 類型,此時(shí)編譯器會(huì)保證 constexpr 變量是常量表達(dá)式(否則編譯報(bào)錯(cuò))。換句話說(shuō),只要看到 constexpr 類型的變量,則一定能夠在編譯期取得結(jié)果,可以用在需要常量表達(dá)式的場(chǎng)景。
int i1 = 42;
constexpr int i2 = i1; // constexpr 變量 'i2' 必須由常量表達(dá)式初始化。不允許在常量表達(dá)式中讀取非 const 變量 'i1'
constexpr int i3 = 42; // i3 是常量表達(dá)式
constexpr int i4 = i3 + 1; // i4 是常量表達(dá)式
constexpr int i5 = getValue(); // 只有 getValue() 是 constexpr 函數(shù)時(shí)才可以,否則編譯報(bào)錯(cuò)
1.5 constexpr 函數(shù)
constexpr 函數(shù)是指能用于常量表達(dá)式的函數(shù)。
需要強(qiáng)調(diào)的是,constexpr 函數(shù)既能用于要求常量表達(dá)式/編譯期常量的語(yǔ)境,也可以作為普通函數(shù)使用。
注意:constexpr 函數(shù)不一定返回常量表達(dá)式!
只有 constexpr 的所有實(shí)參都是常量表達(dá)式/編譯期常量時(shí),constexpr 函數(shù)的結(jié)果才是常量表達(dá)式/編譯期常量。只要有一個(gè)參數(shù)在編譯期未知,那就和普通函數(shù)一樣,在運(yùn)行時(shí)計(jì)算。
constexpr int sum(int a, int b) {
? return a + b;
}
constexpr int i1 = 42;
constexpr int i2 = sum(i1, 52); // 所有參數(shù)都是常量表達(dá)式,sum 的結(jié)果也是常量表達(dá)式,在編譯期求值
int AddThree(int i) {
? return sum(i, 3); // i 不是常量表達(dá)式,此時(shí) sum 作為普通函數(shù)使用
}
為了能保證 constexpr 函數(shù)在編譯時(shí)能隨時(shí)展開(kāi)計(jì)算,constexpr 函數(shù)隱式內(nèi)聯(lián)。內(nèi)聯(lián)函數(shù)和 constexpr 函數(shù)不同于其他函數(shù),允許定義多次,但要保證所有的定義一致。正因如此,內(nèi)聯(lián)函數(shù)和 constexpr 函數(shù)一般定義在頭文件中。
constexpr 限制
因?yàn)樾枰诰幾g期求值,所以 constexpr 函數(shù)有一些限制:返回類型和所有形參的類型必須是字面值類型(literal type)。除了內(nèi)置類型,用戶自定義的類也可以是字面值類型,因?yàn)樗臉?gòu)造函數(shù)和成員函數(shù)也可以是 constexpr 函數(shù)。
C++11 中 constexpr 函數(shù)還有一些額外限制(C++14 沒(méi)有這些限制):
返回值類型不能是 void
函數(shù)體內(nèi)只能有且只有一條 return 語(yǔ)句(但可以用 ? : 三目運(yùn)算符和遞歸)
如果是類的成員函數(shù),則為隱式 const 成員函數(shù)
1.6 使用 constexpr 的好處
編譯器可以保證 constexpr 對(duì)象是常量表達(dá)式(能夠在編譯期取得結(jié)果),而 const 對(duì)象不能保證。如果一個(gè) const 變量能夠在編譯期求值,將其改為 constexpr 能夠讓代碼更清晰易讀
constexpr 函數(shù)可以把運(yùn)行期計(jì)算遷移至編譯期,使得程序運(yùn)行更快(但會(huì)增加編譯時(shí)間)
對(duì)于常量表達(dá)式(編譯期值已知),編譯器可以進(jìn)行更多優(yōu)化,比如放到只讀內(nèi)存中。但這并不是 constexpr 特有的,有的 const 變量也是常量表達(dá)式
1.7 小結(jié)
修飾對(duì)象的時(shí)候,可以把 constexpr 當(dāng)作加強(qiáng)版的 const:const 對(duì)象只表明值不會(huì)改變,不一定能夠在編譯期取得結(jié)果;constexpr 對(duì)象不僅值不會(huì)改變,而且保證能夠在編譯期取得結(jié)果
constexpr 函數(shù)既可以用于編譯期計(jì)算,也可以作為普通函數(shù)在運(yùn)行期使用
散熱風(fēng)扇https://www.uv-semi.com/