首页 > 解决方案 > 为什么在这个 constexpr 函数中允许 std::swap ?

问题描述

我写了一个计算两个数字的 gcd 的函数,它std::swap在第二个参数大于第一个参数的情况下使用。

一段时间后,我意识到std::swap不是, constexpr我的函数仍然编译并成功运行。
我尝试使用 MinGW-w64 8.1.0 和 Visual C++ 2017 并且两者都适用。

我的第一个想法是因为constexpr允许在运行时执行函数,所以我尝试std::integral_constant<int,gcd(32,12)>了,它成功了。

但是,我不能使用我自己的任何非 constexpr 函数(这是我所期望的)。

这是我的测试代码:

#include <utility>

inline void foo() noexcept {
}

template<typename T>
constexpr T gcd(T a, T b) {
    // foo();            // only works with non-constexpr j
    if(a<b) {
        std::swap(a, b); // works for both constexpr i and non-constexpr j
    }
    if(b==0) {
        return a;
    } else {
        return gcd(b, a%b);
    }
}

int main()
{
    constexpr int i = std::integral_constant<int, gcd(32, 12)>::value;
    int j = gcd(32,12);
}

所以,我的问题是:为什么我可以std::swap在我的函数中使用?

标签: c++c++17

解决方案


这是来自 cppreference 的相关引用

constexpr 函数必须满足以下要求:

  • ...
  • 至少存在一组参数值,因此函数的调用可以是核心常量表达式的求值子表达式

有一条不经过的路std::swap(),在哪里a>=b。事实上,因为gcd(32, 12)执行永远不会通过std::swap()

编辑:我看了C++14 草案。第7.1.5 节 constexpr 说明符。第 5 段说:

对于非模板、非默认 constexpr 函数 [...],如果不存在参数值,则函数或构造函数的调用可以是核心常量表达式 (5.20) 的评估子表达式,或者,对于构造函数,某个对象的常量初始化器(3.6.2),程序格式错误;

他们给出的例子是:

constexpr int f(bool b)
{ return b ? throw 0 : 0; } // OK

推荐阅读