代码之家  ›  专栏  ›  技术社区  ›  Chen Li

constexpr函数内部的if-vs-if constexpr

  •  3
  • Chen Li  · 技术社区  · 6 年前

    最近我修改了一些 if constexpr 进入之内 if 在我的constexpr函数中,发现它们仍然可以正常工作,并且可以在编译时进行评估。以下是最低限度的情况:

    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 在编译时必须是已知的,因为它不是类型模板参数,所以 如果constexpr 在这里工作很好。但是,它是一个constexpr函数,所以,iirc,它是 可能的 获取返回值,即使我替换 如果康斯特雷普 具有 如果 :

    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: 不要提 如果 . 所以,IIUC,无论constexpr函数是否包含 如果 在编译时进行评估,即使所有相关变量在编译时都是已知的(如 is_negative 以上)。

    所以,我的结论是:

    • 在C++ 17之前,我们没有 如果康斯特雷普 所以选择是 如果 这意味着不能保证在编译时对constexpr函数进行评估,所有这些都取决于编译器的实现。
    • C++ 17之后, 如果康斯特雷普 如果希望在编译时对constexpr函数进行评估,则首选。

    以上都是我的个人想法,可能是一些重要的遗漏/误解,请随时纠正我。问题仍然没有改变: 如果 如果康斯特雷普 对于预期在编译时进行计算的constexpr函数,应该优先使用它。

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

    2 回复  |  直到 6 年前
        1
  •  10
  •   Nelfeal    6 年前

    在C++ 17之前,我们没有CONSTEPRPR,所以选择是如果,这意味着它不能保证我们的CONTXPR函数在编译时得到评估,都取决于编译器的实现。

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

    在C++ 17之后,如果我们希望CONTXPR函数在编译时得到评估,那么如果CONSTEPRPR是首选的。

    引入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

        2
  •  2
  •   JVApen    6 年前

    两者之间的区别 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);
    }
    

    对于上述代码,编写 如果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的引用。但是,因为一个int使用 如果康斯特雷普 而不是 if ,如果代码依赖于模板参数,则错误路径中的代码只需要在语法上正确。

    在搜索准则时,编译器已经要求:使用 如果康斯特雷普 当其中一个代码路径无效时。使用 如果 取决于参数。

    对于在编译时可以使用两个有效路径计算if条件的情况,请使用 如果康斯特雷普 要要求即使在调试版本中也对其进行优化,请使用 如果 如果您想在调试版本中逐步完成它。

    如果走极端,表达式可能变得过于复杂,编译器无法在生产构建中对其进行优化,在这种情况下 如果康斯特雷普 在这条热门的道路上可能会再次变得有趣。就我个人而言,我还没有遇到这种情况,但是我没有用那么多。