【问题标题】:Difference between "if constexpr()" Vs "if()"“if constexpr()”与“if()”之间的区别
【发布时间】:2017-09-12 01:38:32
【问题描述】:

if constexpr()if() 有什么区别?

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

【问题讨论】:

  • @Rakete1111 constexpr if() 和 if constexpr() 是否相同?
  • @rsp 否,P0292R1 比 P0128R1 更新。还有一个说明是P0292R1修改了P0128R1,所以if constexpr是新语法,不是constexpr if,无效。
  • 堆栈溢出不是要求人们基本上为您写一本书的部分的地方。你做了什么来尝试自己回答这个问题,哪些部分对你来说还不够?

标签: c++ if-statement constexpr c++17


【解决方案1】:

唯一的区别是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();
}

valueint,因此没有length 成员函数,因此编译器会抱怨。编译器看不到 int 永远不会到达该语句,但这没关系,因为编译器无法保证。

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

使用if constexpr而不是if意味着分支(std::is_integral&lt;T&gt;::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 (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();
}

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

【讨论】:

  • 请注意,如果没有if constexpr,您可以有长度调用模板结构并(部分)专门化它以避免代码重复。 if constexpr 的主要好处是 IMO 对程序员来说更“自然”的外观。
  • ...别在意我之前的评论,我看到您确实包含了我在 ("... @ 和 else) 被丢弃。").
  • if (std::integral&lt;T&gt;::value) { // is number 开头的应该是std::is_integral&lt;T&gt;::value
【解决方案2】:

普通的if声明:

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

if constexpr 声明:

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

【讨论】:

  • 参数不一定是 template 参数,不是吗?另外,为什么只有两个子语句?不能有更多吗?
  • @Rakete1111 有效的常量表达式只能依赖于先前初始化的变量值。所以一旦编译器看到if constexpr 语句,它应该已经有足够的信息来评估条件。唯一的例外是当语句出现在模板中时,在这种情况下,在指定模板参数之前可能没有足够的信息。常量表达式不能依赖于普通函数参数,因为它们的值在编译时是未知的。
  • @Rakete1111 if 语句总是有一个或两个子语句。如果你有if ... else if ... else ...,那么你真的有两个 if 语句,一个嵌套在另一个中,每个都有两个子语句。
  • “在模板之外,完全检查丢弃的语句。如果 constexpr 不能替代 #if 预处理指令”。来源:en.cppreference.com/w/cpp/language/if
  • 这有点离题和异端,但是,为什么不只是if?编译器可以决定它是if constexpr 还是只是if。因此,这件事不会影响程序员,但规范必须更改:如果 if 条件评估编译时间,则仅编译 ifelse 代码。但这对程序员来说是透明的。只是好奇。
猜你喜欢
  • 1970-01-01
  • 2012-09-17
  • 2011-06-07
  • 1970-01-01
  • 2016-12-05
  • 2013-03-28
  • 1970-01-01
  • 2023-01-12
  • 2016-04-16
相关资源
最近更新 更多