4

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

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 constexprif

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:都没有提到ifif因此,IIUC,即使所有相关变量在编译时已知(is_negative如上),constexpr 函数是否包含在编译时评估也应该是实现定义的行为。

所以,我的结论是:

  • 在 c ++ 17 之前,我们没有if constexpr,所以选择是if
  • 在 c++17 之后,if constexpr如果我们希望 constexpr 函数在编译时得到评估,则首选。

以上是我的个人想法,可能有一些重要的遗漏/误解,请随时纠正我。问题仍然没有改变:ifand if constexpr,对于期望在编译时评估的 constexpr 函数,应该首选它。

参考: - constexpr 函数中允许什么? - “if constexpr()”与“if()”之间的区别

4

2 回答 2

11

在 c++17 之前,我们没有 if constexpr,所以选择 if,这意味着不能保证我们的 constexpr 函数在编译时被评估,都取决于编译器实现

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

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

引入了 Consexpr if 语句来解决问题。让 constexpr 函数在编译时得到评估不是那个问题。

这是一个constexpr if需要 a 而不是简单的示例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关键字,看看会发生什么(演示)。

另外,请注意,您始终可以使用其他方法解决该问题,但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>{});
}

演示

于 2018-12-17T06:51:42.003 回答
2

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

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-condition 可在编译时使用 2 个有效路径计算的情况,if constexpr即使在调试版本中也需要对其进行优化,if如果您想在调试版本中单步执行,请使用。

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

于 2018-12-17T10:32:23.623 回答