c++ “if constexpr()”与“if()”的区别

s5a0g9ez  于 2023-07-01  发布在  其他
关注(0)|答案(2)|浏览(156)

if constexpr()if()的区别是什么?
何时何地我可以同时使用这两种功能?

lokaqttq

lokaqttq1#

唯一的区别是if constexpr是在编译时计算的,而if不是。这意味着分支可以在编译时被拒绝,因此永远不会被编译。
假设你有一个函数length,它返回一个数字的长度,或者一个具有.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,但是对于很多类型(比如这个例子-每个数字和类都有一个length成员函数),这会导致很多重复的代码。SFINAE也是一种解决方案,但它需要多个函数定义,这使得代码比下面需要的要长得多。
使用if constexpr而不是if意味着分支(std::is_integral<T>::value)将在编译时被评估,如果它是true,那么其他分支(else ifelse)将被丢弃。如果是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 constexpr (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 constexpr (std::is_integral<int>::value) { discarded
    //    return value;                   discarded
    //else                           this branch is taken
        return value.length();
}

因此这两个重载是有效的,代码将成功编译。

sqougxex

sqougxex2#

普通的if语句:

  • 是否在每次控件到达它时对其条件进行评估(如果有的话)
  • 确定执行两个子语句中的哪一个,跳过另一个
  • 要求两个子语句都是格式良好的,而不管在运行时实际选择了哪一个

if constexpr语句:

  • 在提供了所有必需的模板参数后,是否在编译时计算其条件
  • 确定要编译两个子语句中的哪一个,放弃另一个
  • 不要求丢弃的子语句是格式良好的

相关问题