"if constexpr()" 与 "if()" 之间的区别
Difference between "if constexpr()" Vs "if()"
if constexpr()
和if()
有什么区别?
我何时何地可以同时使用它们?
普通if
语句:
- 是否在每次控制到达它时评估它的条件,如果有的话
- 确定要执行两个子语句中的哪一个,跳过另一个
- 无论在运行时实际选择了哪一个,都要求两个子语句都是格式正确的
if constexpr
语句:
- 在提供所有必要的模板参数后,在编译时评估其条件
- 确定要编译两个子语句中的哪一个,丢弃另一个
- 不要求丢弃的子语句格式正确
唯一的区别是 if constexpr
在编译时计算,而 if
不是。这意味着分支可以在编译时被拒绝,因此永远不会被编译。
假设您有一个函数 length
,它是 returns 数字的长度,或者具有 .length()
函数的类型的长度。你不能在一个函数中完成,编译器会抱怨:
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
,但是对于很多类型(比如在这种情况下 - 每个数字和 class 都有一个 length
成员函数),这会导致很多重复的代码。 SFINAE也是一个解决方案,但是它需要多个函数定义,这使得代码比下面需要比较长很多。
使用 if constexpr
而不是 if
意味着分支 (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 个重载是有效的,代码将成功编译。
if constexpr()
和if()
有什么区别?
我何时何地可以同时使用它们?
普通if
语句:
- 是否在每次控制到达它时评估它的条件,如果有的话
- 确定要执行两个子语句中的哪一个,跳过另一个
- 无论在运行时实际选择了哪一个,都要求两个子语句都是格式正确的
if constexpr
语句:
- 在提供所有必要的模板参数后,在编译时评估其条件
- 确定要编译两个子语句中的哪一个,丢弃另一个
- 不要求丢弃的子语句格式正确
唯一的区别是 if constexpr
在编译时计算,而 if
不是。这意味着分支可以在编译时被拒绝,因此永远不会被编译。
假设您有一个函数 length
,它是 returns 数字的长度,或者具有 .length()
函数的类型的长度。你不能在一个函数中完成,编译器会抱怨:
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
,但是对于很多类型(比如在这种情况下 - 每个数字和 class 都有一个 length
成员函数),这会导致很多重复的代码。 SFINAE也是一个解决方案,但是它需要多个函数定义,这使得代码比下面需要比较长很多。
使用 if constexpr
而不是 if
意味着分支 (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 个重载是有效的,代码将成功编译。