32

Given the following code:

#include <iostream>

template <std::size_t N>
struct foo
 { static std::size_t value; };

template <>
std::size_t foo<0>::value = 0u;

template <size_t N>
std::size_t foo<N>::value = 1u + foo<N - 1u>::value;

int main()
 {
   std::cout
      << foo<3u>::value << ' '
      << foo<2u>::value << ' '
      << foo<1u>::value << ' '
      << foo<0u>::value << std::endl;
 }

where the static member value of the template struct foo is recursively initialized, I get different outputs from g++:

3 2 1 0

and from clang++:

1 1 1 0

So seem that g++ initializes foo<N>::value recursively using the initialized value of foo<N-1u>::value where clang++ uses zero for foo<N-1u>::value.

Two questions:

  1. is the preceding code legit or is it Undefined Behavior in some way?
  2. if the preceding code is legit, who's right: g++ or clang++?
Toby Speight
  • 27,591
  • 48
  • 66
  • 103
max66
  • 65,235
  • 10
  • 71
  • 111

2 Answers2

26

It is unspecified. Both compilers are right.

Here are the relevant pieces from cppreference "initialization".

Static initialization

For all other non-local static and thread-local variables, Zero initialization takes place

So for all these variables, they are zero when the program loads. Then:

Dynamic initialization

After all static initialization is completed, dynamic initialization of non-local variables occurs in the following situations:

1) Unordered dynamic initialization, which applies only to (static/thread-local) class template static data members and ... that aren't explicitly specialized.

And these variables match the criteria. And then it says:

Initialization of such static variables is indeterminately sequenced with respect to all other dynamic initialization ....

Which means that any sequence of initialization is fine. Both compilers are correct.

To avoid the issue use constexpr to force a "constant initialization" instead.

Michael Veksler
  • 8,217
  • 1
  • 20
  • 33
3

It is Unspecified.

You are using a construct where you reference a variable definition onto itself - perhaps somewhat analogue to saying int i = i-1. In clang case, it is just using the generic template definition

template <std::size_t N>
struct foo
  { static std::size_t value; };//without specialization this will be ZERO initialized

because it hasn't seen 'itself' like normal template class or function would (as opposed to gcc case).

To sum up:

1) Legit

2) Unspecified

To avoid issues use and specialize the class template instead.

darune
  • 10,480
  • 2
  • 24
  • 62
  • 6
    *"construct where you reference a variable onto itself"* I disagree. `foo::value` and `foo::value` are different variables. – HolyBlackCat Mar 04 '19 at 11:43
  • Compare it to a function taking one argument - are foo(N) and foo(N-1) two different functions ? – darune Mar 04 '19 at 11:44
  • 7
    `foo(N)` and `foo(N-1)` are the same, but `foo()` and `foo()` would be different. – HolyBlackCat Mar 04 '19 at 11:46
  • Which is why I wrote "a construct" - feel free to clarify my answer though - I know it wasn't the greatest for 'correctness' – darune Mar 04 '19 at 11:50
  • @darune The issue with the answer is that "itself"-ness doesn't really come into it. Absent any phrasing for specific case in the standard which says otherwise, `foo<3>` `foo<2>` and `foo<1>` are different types, just like `foo_3`, `foo_2` and `foo_1` would be. As I understand it, the template is a bit of a red herring: you'd (theoretically) have exactly the same sort of issues if you manually unrolled the templates into `foo_3`, etc. (On a standards level, at least - the compilers might treat them differently.) – R.M. Mar 04 '19 at 19:33
  • @R.M. I agree with most of the comment, just not the unrolling - not fully. Conceptually I agree with it, but disagree on the effects of unrolling on init order. Global variables, or fully specialized templates, have a well defined in the translation unit they are defined in: by the definition order. In the code of the OP there is no order, while unrolling-order implies initialization order. – Michael Veksler Mar 04 '19 at 20:29