constexpr函数内部的if vs if constexpr

时间:2018-12-17 06:20:33

标签: c++ constexpr compile-time if-constexpr

最近,我在constexpr函数中将一些if constexpr修改为if,发现它们仍然可以正常工作,并且可以在编译时进行评估。这是最小的情况:

template<int N>
constexpr bool is_negative()
{
    if constexpr  (N >= 0) return false;
    else  return true; 
}
int main()
{
    constexpr  bool v = is_negative<1>();
}

live demo

在上述情况下,N必须是编译类型时必须知道的,因为它是非类型的模板参数,因此if constexpr在这里工作正常。但是,它是一个constexpr函数,因此iirc,即使我将if constexpr替换为if,也可能可能

template<int N>
constexpr bool is_negative()
{
    if  (N >= 0) return false;
    else  return true; 
}
int main()
{
    constexpr  bool v = is_negative<1>();
}

live demo

cppref中,A constexpr function must satisfy the following requirements:中的所有要求都未提及if。因此,对于IIUC,即使在编译时知道所有相关变量(例如上面的if),也必须在编译时评估constexpr函数是否包含is_negative,这是实现定义的行为。

所以,我的结论是:

  • 在c ++ 17之前,我们没有if constexpr,所以选择的是if,这意味着不能保证在编译时对constexpr函数进行求值,这完全取决于编译器实现
  • 在c ++ 17之后,如果我们希望在编译时评估constexpr函数,则首选if constexpr

以上全部是我的个人想法,也许是一些重要的遗漏/误解,可以随时纠正我。问题仍然没有改变:ifif constexpr,对于预期在编译时评估的constexpr函数,应该优先使用它们。

参考: -What is Allowed in a constexpr Function? -Difference between "if constexpr()" Vs "if()"

2 个答案:

答案 0 :(得分:10)

  

在c ++ 17之前,我们没有if constexpr,因此选择if,这意味着不能保证在编译时对constexpr函数进行求值,而这一切都取决于编译器的实现

if语句不是constexpr的事实并不意味着它不能在编译时作为constexpr表达式的一部分进行求值。在您的示例中,在两种情况下,v都在编译时求值,因为它必须是:它是一个常量表达式。这不是实现的定义。

  

在c ++ 17之后,如果希望constexpr函数在编译时求值,则首选constexpr。

Constexpr if语句用来解决问题。在编译时获取constexpr函数以求值并不是问题。

下面是一个示例,其中需要constexpr if而不是简单的if(摘自cppreference):

template <typename T>
auto get_value(T t) {
    if constexpr(std::is_pointer_v<T>)
        return *t; // deduces return type to int for T = int*
    else
        return t;  // deduces return type to int for T = int
}

尝试删除constexpr关键字,看看会发生什么(demo)。

此外,请注意,您始终可以使用其他方法来解决该问题,但是if constexpr具有简洁的优点。例如,下面是使用标签分配的等效get_value

template<typename T>
auto get_value_impl(T t, std::true_type) {
    return *t;
}
template<typename T>
auto get_value_impl(T t, std::false_type) {
    return t;
}

template<typename T>
auto get_value(T t) {
    return get_value_impl(t, std::is_pointer<T>{});
}

Demo

答案 1 :(得分:2)

if constexpr和if之间的区别是表达式是否始终可以在编译时执行。在您的示例中,您使用的是模板参数,因此编写哪个参数并不重要。如果您具有以下代码,则可以注意到差异:

constexpr bool is_negative(int n)
{
    if  (n >= 0) return false;
    else  return true; 
}
int main(int argc, char**)
{
    constexpr  bool v = is_negative(1);
    bool b = is_negative(argc);
    return static_cast<int>(v || b);
}

对于上述代码,无法编写if constexpr。您可以使用运行时值调用该函数。

同样,这无关紧要,因为两个代码路径均有效。当使用具有恒定值的函数时,应该进行常规的编译器优化。

if constexpr的真正利益在于只有一条路径有效时:

template <typename T>
constexpr auto get_value(T t) {
    if constexpr(std::is_pointer_v<T>)
        return *t; // deduces return type to int for T = int*
    else
        return t;  // deduces return type to int for T = int
}

如果T将是一个int,则带有*t的代码路径无效,因为您无法取消引用int。但是,由于使用的是if constexpr而不是if,因此假路径中的代码仅取决于模板参数,因此在语法上仅需正确。

在搜索准则时,编译器已经要求:当其中一个代码路径无效时,请使用if constexpr。根据参数使用if

对于在编译时可以使用2条有效路径计算if条件的情况,请使用if constexpr要求即使在调试版本中也要对其进行优化,如果要逐步执行,请使用if在调试版本中。

如果走极端,表达式可能变得太复杂,以至于编译器无法在生产版本中对其进行优化,在这种情况下,if constexpr在热路径中可能再次变得有趣。就个人而言,我还没有遇到过这种情况,但是我没有使用太多。