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

时间:2019-03-04 11:07:09

标签: c++ templates recursion language-lawyer static-members

给出以下代码:

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

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

3 2 1 0

和从clang ++:

1 1 1 0

似乎g ++使用foo<N>::value的初始化值递归初始化foo<N-1u>::value,其中clang ++对foo<N-1u>::value使用零。

两个问题:

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

2 个答案:

答案 0 :(得分:26)

未指定。两种编译器都是正确的。

以下是cppreference "initialization"中的相关文章。

静态初始化

  

对于所有其他非局部静态变量和线程局部变量,初始化为零

因此对于所有这些变量,在程序加载时它们均为零。然后:

动态初始化

  

所有静态初始化完成后,以下情况会发生非局部变量的动态初始化   情况:

     

1)无序动态初始化,仅适用于   (静态/线程本地)类模板静态数据成员和……未明确专用。

并且这些变量符合条件。然后它说:

  

此类静态变量的初始化不确定地排序   关于所有其他动态初始化....

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

为避免此问题,请使用constexpr强制执行“恒定初始化”。

答案 1 :(得分:3)

它是未指定

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

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

因为它没有像普通的模板类或函数那样(与gcc情况相反)看到“自身”。

总结:

1)合法

2)未指定

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