Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Recursive computation using variable templates - gcc vs clang

Consider the following example:

#include <cstdio>

template <int N>
int fib = fib<N - 1> + fib<N - 2>;

template <> int fib<2> = 1;
template <> int fib<1> = 1;

int main()
{
    std::printf("%d %d %d", fib<4>, fib<5>, fib<6>);
}
  • GCC 7.x, 8.x, 9.x, and 10.x all print out the expected result of 3 5 8.

  • Clang 5.x, 6.x, 7.x, 8.x, 9.x, and 10.x all print out 1 3 4 as a result.

live example on godbolt.org


Clang's behavior is surprising.

Is there any subtle interaction between variable template instantiation, global variables, and recursion in the C++ standard that I am missing?

Or is this a long-standing Clang bug?

By the way, marking fib as constexpr solves the issue (on godbolt.org).

like image 277
Vittorio Romeo Avatar asked Jun 06 '20 16:06

Vittorio Romeo


1 Answers

From [basic.start.dynamic]/1:

Dynamic initialization of a non-local variable with static storage duration is unordered if the variable is an implicitly or explicitly instantiated specialization, is partially-ordered if the variable is an inline variable that is not an implicitly or explicitly instantiated specialization, and otherwise is ordered. [ Note: An explicitly specialized non-inline static data member or variable template specialization has ordered initialization. — end note ]

fib<4>, fib<5> and fib<6> are non-local variables with static storage duration that are implicitly instantiated specializations, so their dynamic initialization is unordered.

The behavior is not undefined; there must be some some unspecified ordering of initialization that produces the output seen (per [basic.start.dynamic]/3.3 the initializations are indeterminately sequenced). In fact, clang initializes in the following order (noting that a variable before dynamic initialization has the value 0 from static initialization):

fib<1> = 1 (actually static-initialized under [basic.start.static]/3)
fib<2> = 1 (similarly)
fib<4> = fib<2> + fib<3> = 1 + 0 = 1
fib<3> = fib<1> + fib<2> = 1 + 1 = 2
fib<5> = fib<3> + fib<4> = 2 + 1 = 3
fib<6> = fib<4> + fib<5> = 1 + 3 = 4

This is equally as valid as gcc (and MSVC) initializating in the order fib<3>, fib<4>, fib<5>, fib<6>.

like image 55
ecatmur Avatar answered Oct 19 '22 18:10

ecatmur