【问题标题】:if vs if constexpr inside constexpr functionif vs if constexpr inside constexpr 函数
【发布时间】:2018-12-17 06:20:33
【问题描述】:

最近我在我的 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

来自cpprefA constexpr function must satisfy the following requirements: 中的所有要求均未提及if。因此,IIUC,即使所有相关变量在编译时已知(如上面的is_negative), constexpr 函数是否包含要在编译时评估的 if 也应该是实现定义的行为。

所以,我的结论是:

  • 在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()"

【问题讨论】:

  • 常量的评估一直是编译时评估。例如。使用模板is_negative()13 会产生条件13 &gt;= 0,编译器会将其解析为1(也没有constexpr)。现代编译器优化了整个条件,但我不知道这是否是强制性的,如果是的话,从哪个版本的标准开始。顺便提一句。 if (N &gt;= 0) return true; else return false;:为什么不return N &gt;= 0;?还是因为minimal reproducible example 的简化?
  • 哎呀,我的错,这是糟糕的风格。我应该发布一个更好的示例...
  • 好的,没问题。 (您也可以说这是由于 MCVE 造成的。这些事情可能看起来有点愚蠢,但只是为了“展示原理”。恕我直言,如果这样说是可以接受的。);-)
  • @Scheff 我发现我只是告诉一个操作员这是很棒的风格 2 个月前stackoverflow.com/a/52826880/6949852XD
  • 我和@Nelfeal 在一起。具有常量的算术或关系操作再次产生常量(编译时评估)。我试图找到相应的。参与 Constant expressions 但失败了,直到我意识到 核心常量表达式 被表达为“反向”,即它们列出了任何内容以使表达式 not 成为核心常量表达式。我需要一点才能意识到这一点...... ;-)

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


【解决方案1】:

在c++17之前,我们没有if constexpr,所以选择if,这意味着不能保证我们的constexpr函数在编译时就被求值,全依赖编译器实现

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

在 c++17 之后,如果我们希望 constexpr 函数在编译时得到评估,则首选 constexpr。

Consexpr 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】:

    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

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

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-11-13
      • 2016-07-24
      • 2019-04-22
      • 1970-01-01
      • 2017-09-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多