if constexpr()
とif()
の違いは何ですか?
いつどこで使用できますか?
通常のifステートメント:
If constexprステートメント:
唯一の違いは、_if constexpr
_はコンパイル時に評価されますが、if
は評価されないことです。これは、コンパイル時にブランチが拒否される可能性があるため、コンパイルされないことを意味します。
数値の長さ、または.length()
関数を持つ型の長さを返す関数length
があるとします。あなたは1つの関数でそれを行うことはできません、コンパイラは文句を言うでしょう:
_template<typename T>
auto length(const T& value) noexcept {
if (std::integral<T>::value) { // is number
return value;
else
return value.length();
}
int main() noexcept {
int a = 5;
std::string b = "foo";
std::cout << length(a) << ' ' << length(b) << '\n'; // doesn't compile
}
_
エラーメッセージ:
_main.cpp: In instantiation of 'auto length(const T&) [with T = int]':
main.cpp:16:26: required from here
main.cpp:9:16: error: request for member 'length' in 'val', which is of non-class type 'const int'
return val.length();
~~~~^~~~~~
_
コンパイラがlength
をインスタンス化すると、関数は次のようになるためです。
_auto length(const int& value) noexcept {
if (std::is_integral<int>::value) { // is number
return value;
else
return value.length();
}
_
value
はint
であるため、length
メンバー関数がないため、コンパイラーは不平を言います。コンパイラーは、int
についてステートメントに到達しないことを認識できませんが、コンパイラーがそれを保証できないため、問題ではありません。
これでlength
を特殊化できますが、多くの型(この場合-length
メンバー関数を持つすべての数値とクラス)の場合、これによりコードが重複します。 SFINAEもソリューションですが、複数の関数定義が必要になるため、コードを以下に比べて必要な長さよりもはるかに長くなります。
if
の代わりに_if constexpr
_を使用すると、ブランチ(_std::is_integral<T>::value
_)がコンパイル時に評価され、true
であれば他のすべてのブランチ(_else if
_およびelse
)は破棄されます。 false
の場合、次のブランチがチェックされ(ここではelse
)、それがtrue
の場合、他のすべてのブランチを破棄する、というように続きます...
_template<typename T>
auto length(const T& value) noexcept {
if constexpr (std::integral<T>::value) { // is number
return value;
else
return value.length();
}
_
これで、コンパイラーがlength
をインスタンス化すると、次のようになります。
_int length(const int& value) noexcept {
//if (std::is_integral<int>::value) { this branch is taken
return value;
//else discarded
// return value.length(); discarded
}
std::size_t length(const std::string& value) noexcept {
//if (std::is_integral<int>::value) { discarded
// return value; discarded
//else this branch is taken
return value.length();
}
_
そして、これらの2つのオーバーロードは有効であり、コードは正常にコンパイルされます。