我想在编译时计算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(…)
语句处理。< / p>
所以问题是,我错了,应该预期这种行为,还是编译错误?用GCC 7.1.0编译。
答案 0 :(得分:7)
为避免错误,请将第二个返回显式放入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>();
}
}
理性:
在封闭函数模板或通用lambda的实例化期间,如果转换条件为true且语句包含constexpr else子语句,则该子语句不会被实例化。
http://open-std.org/JTC1/SC22/WG21/docs/papers/2016/p0128r1.html
它没有说明一个无约束的else块,或者一个不应该运行的块,因此它被实例化,抛出错误。 (注意:铿锵声也失败了)
答案 1 :(得分:5)
不,这不是错误。这里的问题是,即使limit
为0
并且您停止递归,编译器仍然会标记
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>();
}