1

I was trying out the constexpr functions and stumbled upon the below example of implementing Fibonacci numbers

There is no logical difference between fibon2 and fibon1 but I still get the compilation error of exceeding template initializations for the fibon1.

What i'm missing here?

#include <cstdint>
#include <iostream>

template <int32_t x>                                                                                                                                                                                                                                                          
constexpr int32_t fibon2() {                                                                                                                                                                                                                                                  
  if constexpr (x == 0)                                                                                                                                                                                                                                                       
    return 1;                                                                                                                                                                                                                                                                 
  else if constexpr (x == 1)                                                                                                                                                                                                                                                  
    return 1;                                                                                                                                                                                                                                                                 
  else if constexpr (x == 2)                                                                                                                                                                                                                                                  
    return 1;                                                                                                                                                                                                                                                                 
  else                                                                                                                                                                                                                                                                        
    return fibon2<x - 1>() + fibon2<x - 2>();                                                                                                                                                                                                                                 
}                                                                                                                                                                                                                                                                             
template <int32_t x>                                                                                                                                                                                                                                                          
constexpr int32_t fibon1() {                                                                                                                                                                                                                                                  
  if constexpr (x == 0) return 1;                                                                                                                                                                                                                                             
  if constexpr (x == 1) return 1;                                                                                                                                                                                                                                             
  if constexpr (x == 2) return 1;                                                                                                                                                                                                                                             
                                                                                                                                                                                                                                                                              
  return fibon1<x - 1>() + fibon1<x - 2>();                                                                                                                                                                                                                                   
}                                                                                                                                                                                                                                                                             
int32_t fibon3(int32_t x) {                                                                                                                                                                                                                                                   
  if (x == 1) return 1;                                                                                                                                                                                                                                                       
  if (x == 2) return 1;                                                                                                                                                                                                                                                       
  return fibon3(x - 1) + fibon3(x - 2);                                                                                                                                                                                                                                       
}                                                                                                                                                                                                                                                                             
int main() {                                                                                                                                                                                                                                                                  
  
  std::cout << fibon3(2) << std::endl;                                                                                                                                                                                                                                        
  std::cout << fibon2<2>() << std::endl;                                                                                                                                                                                                                                      
  std::cout << fibon1<2>() << std::endl;                                                                                                                                                                                                                                      
  
  return 0;                                                                                                                                                                                                                                                                   
} 
paddy
  • 60,864
  • 6
  • 61
  • 103
user3703826
  • 87
  • 1
  • 6

2 Answers2

3

The return in fibon1 is not under constexpr and thus has to be compiled regardless of the template argument provided.

Clang show this nicely: https://godbolt.org/z/577f15Kv1

If you put the return inside its own if constexpr then your fibon1 will compile:

template <int32_t x>
constexpr int32_t fibon1() {
  if constexpr (x == 0) return 1; 
  if constexpr (x == 1) return 1;
  if constexpr (x == 2) return 1;
  if constexpr (x > 2)
      return fibon1<x - 1>() + fibon1<x - 2>();
}
Amir Kirsh
  • 12,564
  • 41
  • 74
2

The last return in fibon2 is in a constexpr-if even though there's only an else there. It's the same as if you'd made it:

  else if constexpr (x == 2)
    return 1;
  else if constexpr(true)                      // <- like this
    return fibon2<x - 1>() + fibon2<x - 2>();

That's not the case in fibon1 which is why it fails. If you want a freestanding if constexpr for it, then

template <int32_t x>
constexpr int32_t fibon1() {
  if constexpr (x == 0) return 1; // shouldn't this be 0 ?
  if constexpr (x == 1) return 1; 
  if constexpr (x == 2) return 1; // this doesn't seem correct if fibon1<0> => 1

  // added constexpr-if:
  if constexpr (x < 0 || x > 2) return fibon1<x - 1>() + fibon1<x - 2>();
}

Note that the above will fail if you supply a negative value as a template parameter as it will never reach one of your terminating conditions. Either the recursion will be to deep or you'll get a signed integer overflow. If you instead want symmetry around 0, you can simply negate the template parameter and return value in case the template parameter is negative:

template <int32_t x>
constexpr int32_t fibon1() {
    if constexpr (x < 0)
        return -fibon1<-x>();            // like this
    else if constexpr (x == 0 || x == 1) // assuming you want 0 for input 0
        return x;
    else
        return fibon1<x - 1>() + fibon1<x - 2>();
}
Ted Lyngmo
  • 93,841
  • 5
  • 60
  • 108