6

我想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正在处理 –case if constexpr(…)

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

4

2 回答 2

7

为避免该错误,请将第二个 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

合理的:

在封闭函数模板或泛型 lambda 的实例化期间,如果转换后的条件为真并且语句包含 constexpr else 子语句,则不会实例化该子语句。

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

它没有说明不受约束的 else 块或不应该运行的块,因此它被实例化,抛出错误。(注意:在铿锵声中也失败)

于 2017-07-31T20:13:30.850 回答
5

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

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

因为它是无条件的。您需要将其放入 else 块中才能使其仅在limitis not时进行编译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>();
}
于 2017-07-31T20:15:37.810 回答