4

If I write a compile-time factorial function using specialisation, the following code can suffice, and will correctly provide 120 as the result of fact1<5>():

template <size_t N>
constexpr size_t fact1() { return N*fact1<N-1>(); }

template <>
constexpr size_t fact1<0>() { return 1; }

However, with a single function body and the ternary operator, as in the following code, G++ 4.7 and Clang++ 3.2 both exceed their maximum template instantiation depth. It appears that 1 is never returned from fact2. Why is it that this definition of fact2<5>() doesn't return 120?

template <size_t N>
constexpr size_t fact2() { return N==0 ? 1 : N*fact2<N-1>(); }
Xeo
  • 129,499
  • 52
  • 291
  • 397
user2023370
  • 10,488
  • 6
  • 50
  • 83
  • 2
    As I understand it, the whole point of `constexpr` is that you don't *have* to use template parameters and template recusion anymore. That you can just have `fact` which takes an integer and returns the factorial. Just like a regular function. – Nicol Bolas Sep 03 '12 at 21:40
  • 1
    [Here](http://stackoverflow.com/a/11746541/1173542) is constexpr factorial. If you need not a number but a type, you can wrap it in std::numeric_constant – Leonid Volnitsky Sep 04 '12 at 03:44
  • @LeonidVolnitsky Thankyou. I can't find a reference for std::numeric_constant though. – user2023370 Sep 04 '12 at 19:41
  • @user643722 -- wrong name - it is called [integral_constant](http://en.cppreference.com/w/cpp/types/integral_constant). Given a number, it will return a type: `integral_constant::type` – Leonid Volnitsky Sep 05 '12 at 04:07

1 Answers1

7

The problem here is that no matter what, fact2<N-1> will always be instantiated (even non-executed paths need to be compiled, see Effective C++, I think Item 47 or 48). You need to, somehow, make it only instantiate the next function if you're not at the end. One way would be to just say "screw templates" and go the usual constexpr way as @NicolBolas says in his comment.

Another would be using one of the techniques used in this similar question.

Community
  • 1
  • 1
Xeo
  • 129,499
  • 52
  • 291
  • 397
  • My example isn't good enough. I actually need to return a type which uses N as a template argument. Correct answer though. Thanks. – user2023370 Sep 03 '12 at 22:27
  • 1
    The standard workaround is to use specialization to establish the recursion base case, as was done in the first example. – Raymond Chen Sep 04 '12 at 03:49