使用变量模板的递归计算 - gcc 与 clang

Recursive computation using variable templates - gcc vs clang

本文关键字:gcc clang 递归计算 变量      更新时间:2023-10-16

请考虑以下示例:

#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 和 10.x 都打印出3 5 8的预期结果。

  • 因此,Clang 5.x、6.x、7.x、8.x、9.x 和 10.x 都打印出1 3 4

godbolt.org 上的活生生的例子


Clang的行为令人惊讶。

变量模板实例化、全局变量和我缺少的C++标准中的递归之间是否存在任何微妙的交互?

或者这是一个长期存在的叮当虫?

顺便说一下,将fib标记为constexpr可以解决问题(在 godbolt.org 上(。

From [basic.start.dynamic]/1:

如果具有静态存储持续时间的非局部变量是隐式或显式实例化的专用化,则动态初始化是

无序的;如果变量是不是隐式或显式实例化的内联变量,则动态初始化是部分有序的,否则是有序的。[注意:显式专用的非内联静态数据成员或变量模板专用化已有序初始化。

fib<4>fib<5>fib<6>是具有静态存储持续时间的非局部变量,它们是隐式实例化的专用化,因此它们的动态初始化是无序的。

行为不是未定义的;必须有一些未指定的初始化顺序来生成看到的输出(根据 [basic.start.dynamic]/3.3,初始化是不确定排序的(。实际上,clang 按以下顺序初始化(注意动态初始化之前的变量在静态初始化中的值为 0(:

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

这与 gcc(和 MSVC(按顺序初始化fib<3>fib<4>fib<5>fib<6>相同。