constexpr究竟有什麼用?
主要大概幾個場景(超程式設計中用的比較多):
比如用來簡化元函式
template
constexpr bool is_same_v = std::is_same::value;
呼叫的時候可以直接
auto x = is_same_v
用於超程式設計的常成員:
template
struct is_same {
static constexpr int value = false;
}
template
struct is_same
static constexpr int value = true;
}
這樣就可以透過
is_same
來呼叫這個元函式。
以及修飾常函式,
constexpr int add(int a, int b) { return a + b; }
修飾分支:
if constexpr () {}
其實你把constexpr修飾的東西看成元函式的變數就好理解了
constexpr 的主要用處有
拓寬「常量表達式」的範圍
提供顯式「要求」表示式編譯時(compile-time)求值的方法
為什麼要拓寬「常量表達式」的範圍,從原本標準庫中的很多尷尬之處就可以看出:
比如我們都知道
INT_MAX
是 C 語言的遺物,C++ 則更希望大家使用
std::numeric_limits
來拿
int
型的上限。然而不幸的是,後者是個函式呼叫而不是常量,使用起來可能需要花更多效能上的心思,沒有前者那麼自由。
又比如標準檔案流,它的建構函式可以帶上這樣的第二個引數:
std
::
fstream
foo
(
“foo。txt”
,
std
::
ios
::
in
|
std
::
ios
::
out
);
這個引數是
openmode
型別的,是由實現具體定義的一種 Bitmask 型別。出於型別安全的考慮,通常用列舉值實現而不是整型。但是這樣一來就有個問題,同樣是寫
std::ios::in | std::ios::out
,如果用整型的話可以作為常量表達式使用,而為了型別安全考慮換用列舉實現(尤其是過載
|
運算子)後,就再也不可能是常量表達式了。
inline
openmode
operator
|
(
openmode
lhs
,
openmode
rhs
)
{
return
openmode
(
int_type
(
lhs
)
|
int_type
(
rhs
));
}
明明是這樣簡單的函式,可是對它的呼叫卻不是常量表達式。這就讓委員會陷入了必須在「型別安全」和「效率」裡二選一的尷尬境地。
標準庫裡會遇到這樣的問題,大家日常使用也會遇到。加之標準委員會很想借此機會把原本標準中對於「常量表達式」(尤其是整型常量表達式)複雜的定義重構簡化,引入 constexpr 就很合情合理了。
(此處解釋的是把「對某些簡單函式的呼叫」加入「常量表達式」定義的出發點。其它的比如 constexpr 建構函式的用途,套路是一樣的,請自行腦補。)
說到這裡,
「constexpr 函式」並不能和「編譯時求值」劃等號
,它只表達了「這個函式具備這樣的能力」。
所以才有了 constexpr 的第二個功能:「顯式『要求』表示式編譯時求值」。把它放到變數定義前,那麼用來初始化這個變數的表示式「必須」是常量表達式,否則報錯。
constexpr 函式只有同時滿足
所有引數都是常量表達式
返回的結果被用於常量表達式(比如用於初始化 constexpr 資料)
才會觸發編譯時求值。如果只有引數是常量表達式而結果不是,那麼是否觸發編譯時求值取決於具體實現。
C++17 的 constexpr if 雖然嚴格意義上不是 constexpr 而是 if 的一部分,但既然名字裡帶 constexpr,也順便提一下。
constexpr if 的主要用途是簡化模板程式碼(這也意味著除非你是庫作者或者模板狂魔,很少會用到)。很多原本需要繞彎藉助 SFINAE 或者型別 Tag 來實現,需要拆成 N 個函式的功能,可以藉助 constexpr if 寫到一個函數里。
(還有一個用途是可以代替類似
#if
的功能,但……我覺得是邪道。)
// 判斷T是否有N個成員變數, 編譯期實現, 遞迴情形
template
struct HasParam {
constexpr operator bool () {
if constexpr (is_tuple
return N == std::tuple_size
} else {
return HasParam
}
}
};
// 判斷T是否有N個成員變數, 遞迴終止情形
template
struct HasParam
constexpr operator bool () {
return is_constructible
}
};
當然有用,constexpr函式可以實現編譯期的判斷, 從而更好的去做 SFINAE 型別檢查 特例化等操作。
這是一段判斷struct內有幾個成員的函式。而透過編譯期獲取類成員個數,我們就可以去做C++17的新特性structured-binding,否則在類成員數目不確定時,編譯將不能透過。
除此而外constexpr if 還以替代之前std::enable_if + type_traits的醜陋繁瑣的trick操作,大大簡化程式碼,增加程式碼的可讀性,個人建議能加就加
就是個高階點的【顯式】常量摺疊標記
沒什麼卵用,不知道為什麼有人非要在模板引數的位置呼叫複雜函式
constexpr最佳化?你可給我歇會吧
今天剛好用到這個東西。是參考這篇文章做的:
CSDN-專業IT技術社群-登入
我想要給一個string做switch操作,但是c++不能對string進行switch,只能是int或enum或者class。
所以得把string轉換成int,算它的Hash值就ok了。
我想要的效果是:
switch(hash(a)){
case hash(“a”): ;
case hash(“b”): ;
}
這樣。但是hash(“a”)不屬於常量,不能case。只好把函式變成了constexpr才解決了問題。
雖然用QStringList和Qmap、Qhash都能把string轉換成int值,但我實在不想 case 0: 、case 1:不太直觀。