可能的模板和 constexpr——如果不兼容

Possible template & constexpr–if incompatibility

我想在编译时计算 e 值(别担心,不是作业),但出了点问题。

template<size_t limit = 3, class result = std::ratio<0, 1>, size_t factorial = 1, size_t count = 1>
constexpr double e_impl() {
    if constexpr(limit == 0) {
        return static_cast<double>(result{}.num) / result{}.den;
    }
    return e_impl<limit - 1, std::ratio_add<result, std::ratio<1, factorial>>, factorial * count, count + 1>();
}

虽然计算值正确,但编译器会抛出有关模板溢出的错误。 limit 变量似乎超出了范围(低于 0),但它不应该发生,因为 0 案例正在由 if constexpr(…) 语句处理。

所以问题是,我错了吗,这种行为应该是预料之中的,还是编译器错误?使用 GCC 7.1.0 编译。

为避免错误,将第二个 return 显式放入 else 分支:

template<size_t limit = 3, class result = std::ratio<0, 1>, size_t factorial = 1, size_t count = 1>
constexpr double e_impl() {
    if constexpr(limit == 0) {
        return static_cast<double>(result{}.num) / result{}.den;
    }
    else
    {
      return e_impl<limit - 1, std::ratio_add<result, std::ratio<1, factorial>>, factorial * count, count + 1>();
    }
}

https://godbolt.org/g/PdV7m7

理性:

During an instantiation of the enclosing function template or generic lambda, if the converted condition is true and the statement includes a constexpr else substatement, that substatement is not instantiated.

http://open-std.org/JTC1/SC22/WG21/docs/papers/2016/p0128r1.html

它没有提到一个不受约束的 else 块,或者一个不应该 运行 的块,所以它被实例化,抛出错误。 (注意:在 clang 上也失败)

不,这不是错误。这里的问题是,即使 limit0 并且您停止递归,编译器仍然会消除

return e_impl<limit - 1, std::ratio_add<result, std::ratio<1, factorial>>, factorial * count, count + 1>();

因为它是无条件的。您需要将其放入 else 块中,以便仅在 limit 不是 0.

时进行编译
template<size_t limit = 3, class result = std::ratio<0, 1>, size_t factorial = 1, size_t count = 1>
constexpr double e_impl() {
    if constexpr(limit == 0)
        return static_cast<double>(result{}.num) / result{}.den;
    else
        return e_impl<limit - 1, std::ratio_add<result, std::ratio<1, factorial>>, factorial * count, count + 1>();
}