The code
#include <iostream>
using namespace std;
template<int n> struct Fibo { static int x; };
template<> int Fibo<0>::x = 1;
template<> int Fibo<1>::x = 1;
template<int n> int Fibo<n>::x = Fibo<n-1>::x + Fibo<n-2>::x; //marked line
int main() {
cout << Fibo<5>::x << endl;
cout << Fibo<4>::x << endl;
cout << Fibo<3>::x << endl;
cout << Fibo<2>::x << endl;
cout << Fibo<1>::x << endl;
cout << Fibo<0>::x << endl;
return 0;
}
outputs
0
0
1
2
1
1
in VC++. (According to user M M. it compiles as expected in gcc). When the compiler gets to the marked line with n=5
it doesn't compile that same line again for n=4
, but just treats Fibo<4>::x
as if it were declared with
template<> int Fibo<4>::x; // x defaults to 0
Why is that? Why does it work as expected when using
template<int n> struct Fibo { enum { x = Fibo<n-1>::x + Fibo<n-2>::x }; };
template<> struct Fibo<0> { enum { x = 1 }; };
template<> struct Fibo<1> { enum { x = 1 }; };
instead, but not with a static variable? And how do you fix the first code (without enum
)?
The Standard is very clear on this:
14.7.1 Implicit instantiation [temp.inst]
9 The implicit instantiation of a class template does not cause any static data members of that class to be implicitly instantiated.
All the calls in main()
to your Fibo<n>::x
for n > 1
, are explicit instantiations, that through the Fibonnaci recursion will implicitly instantiate Fibo<n-1>
and Fibo<n-2>
but not their members x
. This means that at those points, the static
members x
will be evaluated to their default initialization of 0
. For n=1
and n=0
, the compiler will see the explicit initialization values of 1. So effectively, you get the following computation
Fibo<5>::x --> Fibo<4>::x + Fibo<3>::x --> 0 + 0 = 0
Fibo<4>::x --> Fibo<3>::x + Fibo<2>::x --> 0 + 0 = 0
Fibo<3>::x --> Fibo<2>::x + Fibo<1>::x --> 0 + 1 = 1
Fibo<2>::x --> Fibo<1>::x + Fibo<0>::x --> 1 + 1 = 2
Fibo<1>::x --> 1
Fibo<0>::x --> 1
You need to instantiate the static member x
before evaluating the Fibonacci recursion. You can do this through a static const int
or enum
member x
, or through a function (possibly constexpr
in C++11) as shown by @Jarod42.