Considering the following example:

```
#include<iostream>
template<int n>
struct fibonacci {
static const int value = n < 0 ? 0 : fibonacci<n-1>::value + fibonacci<n-2>::value;
};
template<>
struct fibonacci<1> {
static const int value = 1;
};
template<>
struct fibonacci<0> {
static const int value = 0;
};
int main() {
std::cout << fibonacci<-1>::value << std::endl;
return 0;
}
```

I am familiar with lazy evaluation in C++, and would expect that the second branch of the if statement the generic fibonacci template would not be evaluated, when an argument < 0 is passed. However, compiling the code still results in an infinite loop from that branch:

```
Fibonacci.cpp: In instantiation of ‘const int fibonacci<-900>::value’:
Fibonacci.cpp:5:58: recursively required from ‘const int fibonacci<-2>::value’
Fibonacci.cpp:5:58: required from ‘const int fibonacci<-1>::value’
Fibonacci.cpp:20:33: required from here
Fibonacci.cpp:5:58: fatal error: template instantiation depth exceeds maximum of 900 (use ‘-ftemplate-depth=’ to increase the maximum)
5 | static const int value = n < 0 ? 0 : fibonacci<n-1>::value + fibonacci<n-2>::value;
| ^~~~~
```

Why is this the case? Is it specific to something related to templated structs?

Stack Overflow Asked by gcalin on January 8, 2021

4 AnswersI am familiar with lazy evaluation in C++, and would expect that the second branch of the if statement the generic fibonacci template would not be evaluated, when an argument < 0 is passed.

It doesn't need to be evaluated. But we aren't dealing with evaluation here. We are dealing with template instantiation. You used `fibonacci<n-1>::value`

, and that requires the complete object type `fibonacci<n-1>`

to be instantiated. The type has to be checked, to see if it has a member `value`

that can be used in such an expression.

Instantiating a class template causes the declarations of its members to be instantiated. The declaration of the static data member contains an initializer, which must therefore be instantiated as well. So we hit the need to instantiate the template recursively.

Simply naming `fibonacci<n-1>`

won't cause it to be instantiated (think forward declarations). If you want to delay the instantiation, you must delay using those types in a way that requires their definition (such as accessing a member).

The old meta-programming trick for this (that is very in line with functional programming) involves helper templates.

```
template<class L, class R>
struct add {
static constexpr auto value = L::value + R::value;
};
template<int n>
struct fibonacci {
static const int value = std::conditional_t<(n < 0), fibonacci<0>, add<fibonacci<n-1>, fibonacci<n-2>>>::value;
};
```

`std::conditional_t`

will choose a type based on the condition. Then, the `::value`

of that type (and only that type) is accessed. So nothing is fully instantiated until actually needed.

Correct answer by StoryTeller - Unslander Monica on January 8, 2021

Right answers has already been provided by Artefacto and Story Teller - Unslander Monica .

I am just trying to explain further with the help of cppinsights

For explanation purpose consider the original code with a little modification:

```
...
template<int n>
struct fibonacci {
static const int value = n > 0 ? 0 : fibonacci<n-1>::value + fibonacci<n-2>::value;
};
...
int main() {
std::cout << fibonacci<3>::value << std::endl; // modified parameter from -1 to 3 to avoid compilation failure by recursive instantiation
return 0;
}
```

Here is what the generated by compiler will look like

```
...
#ifdef INSIGHTS_USE_TEMPLATE
template<>
struct fibonacci<3>
{
static const int value = 3 > 0 ? 0 : fibonacci<2>::value + fibonacci<1>::value;
};
#endif
/* First instantiated from: insights.cpp:5 */
#ifdef INSIGHTS_USE_TEMPLATE
template<>
struct fibonacci<2>
{
static const int value = 2 > 0 ? 0 : fibonacci<1>::value + fibonacci<0>::value;
};
#endif
...
int main()
{
std::cout.operator<<(fibonacci<2>::value).operator<<(std::endl);
return 0;
}
```

It is clear from above generated code that even if the expression `3 > 0`

is true still compiler instantiated the templates in `false`

expression for `n = 2`

and `n = 1`

Now look at the code of `if constexpr`

shared by Artefacto. i.e.

```
template<int n>
struct fibonacci {
static const int value = []() {
if constexpr (n < 0) {
return 0;
} else {
return fibonacci<n-1>::value + fibonacci<n-2>::value;
}
}();
};
```

Considering template parameter as `-1`

. Here is how compiler will interpret it

```
#ifdef INSIGHTS_USE_TEMPLATE
template<>
struct fibonacci<-1>
{
class __lambda_5_30
{
public:
inline /*constexpr */ int operator()() const
{
if constexpr(-1 < 0) {
return 0;
}
}
using retType_5_30 = int (*)();
inline /*constexpr */ operator retType_5_30 () const noexcept
{
return __invoke;
};
private:
static inline int __invoke()
{
if constexpr(-1 < 0) {
return 0;
}
}
} __lambda_5_30{};
static const int value = __lambda_5_30.operator()();
class __anon_5_30;
};
#endif
```

It's clear from above code that compiler didn't even considering the else part of `if constexpr`

. That's why this code will work completely fine.

Answered by The Philomath on January 8, 2021

When `fibonacci`

is instantiated with some value of `n`

, all expressions used inside this instantiation must be compiled as well. This means that any templates that are used must also be instantiated. This is necessary even if the expression containing the template instantiation is never evaluated.

The only way to avoid instantiation of a template inside an expression is to not compile the expression at all. This allows you to avoid instantiating the templates with incorrect arguments.

You can do this by using `if constexpr`

from C++17:

```
template<int n>
struct fibonacci {
static const int value = [] {
if constexpr (n < 0) return 0;
else return fibonacci<n-1>::value + fibonacci<n-2>::value;
}();
};
```

Here's a demo.

Answered by cigien on January 8, 2021

You can use `if constexpr`

:

```
template<int n>
struct fibonacci {
static const int value = []() {
if constexpr (n < 0) {
return 0;
} else {
return fibonacci<n-1>::value + fibonacci<n-2>::value;
}
}();
};
```

Answered by Artefacto on January 8, 2021

0 Asked on November 4, 2021 by user12711263

1 Asked on November 4, 2021

1 Asked on November 4, 2021 by greenycodeskii

1 Asked on November 4, 2021 by sravya-jasthi

1 Asked on November 4, 2021 by xmh0511

1 Asked on November 4, 2021

1 Asked on November 4, 2021 by ios

1 Asked on March 10, 2021 by newaccessuser

1 Asked on March 8, 2021 by hasan-h

1 Asked on March 8, 2021 by saarang-tiwari

1 Asked on March 6, 2021 by iamgrooot

1 Asked on March 5, 2021 by mohamed-mostapha

1 Asked on March 5, 2021 by andreas-hunter

1 Asked on March 5, 2021 by jack_dev

0 Asked on March 4, 2021 by jpfotoz

Get help from others!

Recent Answers

- kjetil b halvorsen on How to test consistency of responses?
- Philipp on How do i draw a ray in unity
- DMGregory on MouseLook Script “Pops” back to the last value when the script is enabled after being disabled or destroyed
- Justin Markwell on Unity app crashes when using unmodified custom Android manifest (didn’t find class “UnityPlayerActivity”)
- eric_kernfeld on How to test consistency of responses?

Recent Questions

- MouseLook Script “Pops” back to the last value when the script is enabled after being disabled or destroyed
- Unity app crashes when using unmodified custom Android manifest (didn’t find class “UnityPlayerActivity”)
- How do i draw a ray in unity
- How to test consistency of responses?
- How can I understand these variograms?

© 2022 AnswerBun.com. All rights reserved.