“if constexpr()”与“if()”之间的区别

问题描述 投票:0回答:2

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

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

c++ if-statement c++17 constexpr if-constexpr
2个回答
124
投票

唯一的区别是

if constexpr
在编译时计算,而
if
则不然。这意味着分支可以在编译时被拒绝,因此永远不会被编译。


假设您有一个函数

length
,它返回数字的长度,或者具有
.length()
函数的类型的长度。你不能在一个函数中完成它,编译器会抱怨:

template<typename T>
auto length(const T& value) noexcept {
    if (std::is_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 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 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();
}

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


78
投票

普通

if
声明:

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

if constexpr
声明:

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