g++ 和 clang++ 静态成员递归初始化的不同行为

g++ and clang++ different behaviour with recursive initialization of a static member

给定以下代码:

#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;
 }

递归初始化模板结构 foo 的静态成员 value,我从 g++ 得到不同的输出:

3 2 1 0

来自 clang++:

1 1 1 0

看来 g++ 使用 foo<N-1u>::value 的初始化值递归初始化 foo<N>::value,其中 clang++ 使用零表示 foo<N-1u>::value

两个问题:

  1. 前面的代码是合法的还是在某种程度上是未定义的行为?
  2. 如果前面的代码是合法的,谁是对的:g++ 还是 clang++?

未指定。两个编译器都是对的。

以下是 cppreference "initialization".

的相关文章

静态初始化

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

所以对于所有这些变量,它们在程序加载时都为零。那么:

动态初始化

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.

并且这些变量符合标准。然后它说:

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

这意味着任何初始化顺序都可以。两个编译器都是正确的。

为避免此问题,请使用 constexpr 来强制使用 "constant initialization"。

未指定

您正在使用 构造,您在其中将变量定义引用到自身 - 也许有点类似于说 int i = i-1。在 clang 情况下,它只是使用通用模板定义

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

因为它还没有 见过 'itself' 像普通模板 class 或函数那样(与 gcc 案例相反)。

总结一下:

1) 合法

2) 未指定

为避免出现问题,请使用 并专门化 class 模板。