"if constexpr()"与"if()"之间的区别

msc*_*msc 21 c++ if-statement constexpr c++17

if constexpr()和之间有什么区别if()

我何时何地可以同时使用它们?

Rak*_*111 27

唯一的区别是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
}
Run Code Online (Sandbox Code Playgroud)

错误信息:

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();
            ~~~~^~~~~~
Run Code Online (Sandbox Code Playgroud)

那是因为当编译器实例化时length,该函数将如下所示:

auto length(const int& value) noexcept {
    if (std::is_integral<int>::value) { // is number
        return value;
    else
        return value.length();
}
Run Code Online (Sandbox Code Playgroud)

value是一个int,因此没有length成员函数,所以编译器抱怨.编译器无法看到永远不会达到该语句int,但无关紧要,因为编译器无法保证.

现在你可以专门化length,但是对于很多类型(比如在这种情况下 - 每个数字和带有length成员函数的类),这会产生大量重复的代码.SFINAE也是一个解决方案,但它需要多个函数定义,这使得代码比下面需要的代码要长很多.

使用if constexpr而不是if意味着branch(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();
}
Run Code Online (Sandbox Code Playgroud)

现在,当编译器实例化时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();
}
Run Code Online (Sandbox Code Playgroud)

所以这两个重载是有效的,代码将成功编译.

  • 请注意,如果没有 `if constexpr`,您可以使用 length 调用模板结构,并且(部分)专门化它以避免代码重复。`if constexpr` 的主要好处是 IMO 对程序员来说更“自然”的外观。 (2认同)
  • ```if (std::integral&lt;T&gt;::value) { // is number``` 开头应该是 ```std::is_integral&lt;T&gt;::value``` (2认同)

Bri*_*ian 24

普通的if语句:

  • 如果有的话,每次控制到达时都会评估其状况
  • 确定要执行的两个子语句中的哪一个,跳过另一个子语句
  • 无论在运行时实际选择哪个子语句,都要求两个子语句格式良好

if constexpr声明:

  • 一旦提供了所有必要的模板参数,就在编译时评估其条件
  • 确定要编译的两个子语句中的哪一个,丢弃另一个子语句
  • 不要求丢弃的子语句格式良好

  • “在模板之外,将完全检查被丢弃的语句。如果 constexpr 不能替代 #if 预处理指令”。来源:https://en.cppreference.com/w/cpp/language/if (4认同)
  • @Rakete1111有效的常量表达式只能取决于先前初始化的变量值.因此,一旦编译器看到`if constexpr`语句,它应该已经有足够的信息来评估条件.唯一的例外是当语句出现在模板中时,在这种情况下,在指定模板参数之前可能没有足够的信息.常量表达式不能依赖于普通函数参数,因为它们的值在编译时是未知的. (3认同)
  • @ Rakete1111 if语句总是有一个或两个子语句.如果你有`if ... else if ... else ...`那么你真的有两个if语句,一个嵌套在另一个语句中,每个语句都有两个子语句. (2认同)