Possible pattern and constexpr- if incompatible

I wanted to compute the value of e at compile time (don't worry, not homework), but something went wrong.

 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>(); } 

While the calculated values ​​are correct, the compiler throws an error about template overflow. It seems that the variable limit is out of range (below 0 ), but this should not happen, since operator 0 processed by the operator if constexpr(…) .

So, the question is, am I mistaken and should this behavior be expected, or is this a compiler error? Compiled with GCC 7.1.0.

+5
source share
2 answers

To avoid the error, put the second return explicitly in the else branch:

 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

Rational:

When creating a template for a closing function or a common lambda, if the transformed condition is true and the statement includes the constexpr else constant, this substation is not created.

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

It does not say anything about a block without restrictions or a block that should not start, so it receives an instance, causing an error. (Note: clang also fails)

+6
source

No, this is not a mistake. The problem here is that even when limit is 0 and you stop the recursion, the compiler still prints

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

since it is unconditional. You need to put it in an else block to get it only for compilation, if limit 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>(); } 
+5
source

Source: https://habr.com/ru/post/1270408/


All Articles