【问题标题】:Recursive computation using variable templates - gcc vs clang使用变量模板的递归计算 - gcc vs clang
【发布时间】:2020-06-06 16:47:41
【问题描述】:

考虑以下示例:

#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。

live example on godbolt.org


Clang 的行为令人惊讶。

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

或者这是一个长期存在的 Clang 错误?

顺便说一句,将fib 标记为constexpr 可以解决问题(on godbolt.org)。

【问题讨论】:

  • 我的直觉说这是静态初始化命令的失败......在这种情况下它是否被标准中的某些东西允许或者它是一个 Clang 错误,我不确定。但是constexpr 修复它是有意义的,因为它在概念上强加了一个排序。
  • @cdhowie 当它们以错误的顺序初始化时会不会导致无限递归?
  • @idclev463035818 否。问题是,当实例化fib&lt;4&gt; 导致fib&lt;3&gt; 被实例化时,标准保证 fib&lt;3&gt; 在fib&lt;4&gt; 之前初始化?如果不是,fib&lt;4&gt; 在初始化之前使用它并读取一个不确定的值。
  • 仅供参考,clang-cl 给出了这个(三次,每个实例一次):警告:声明需要全局构造函数 [-Wglobal-constructors]
  • @cdhowie 你能用单个编译单元搞定静态初始化命令吗?

标签: c++ recursion c++14 language-lawyer variable-templates


【解决方案1】:

来自[basic.start.dynamic]/1:

如果变量是隐式或显式实例化的特化,则具有静态存储持续时间的非局部变量的动态初始化是无序的,如果变量是不是隐式或显式实例化的特化的内联变量,则它是部分排序的,并且否则下令。 [ 注意:显式特化的非内联静态数据成员或变量模板特化已排序初始化。 — 尾注 ]

fib&lt;4&gt;、fib&lt;5&gt; 和 fib&lt;6&gt; 是具有静态存储持续时间的非局部变量,它们是隐式实例化的特化,因此它们的动态初始化是无序的。

行为不是未定义的;必须有一些未指定的初始化顺序会产生所见的输出(根据[basic.start.dynamic]/3.3,初始化的顺序是不确定的)。实际上,clang 的初始化顺序如下(注意动态初始化之前的变量有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

这与按fib&lt;3&gt;、fib&lt;4&gt;、fib&lt;5&gt;、fib&lt;6&gt; 的顺序初始化的 gcc(和 MSVC)同样有效。

【讨论】:

  • 由于非显式特化的动态初始化是无序的,当一个(非显式)特化的初始化使用另一个尚未 -被初始化(非显式)特化;特别是由于非显式特化的不确定顺序初始化而导致的“可能发生”场景?
  • @dfri 见this。特别是注释中的示例。编译器可以使用动态初始化或静态初始化的值。实际上答案引用了这个,没有链接到它:)
  • @dfri SIOF 实际上不是 UB(好吧,只要不涉及线程);它可以在不变量建立之前通过访问类成员导致UB。这里我们有简单的int 没有内部不变量的对象;它们要么是0(来自静态初始化),要么是它们的动态初始化值。
  • @idclev463035818 这意味着要进行一致的初始化,它们需要进行常量初始化,这实际上意味着它们需要是 constexpr。
  • @idclev463035818 另请注意,根据my comment above,这与变量模板没有任何特殊关系;类模板的非常量静态数据成员也是如此;每个使用递归初始化的非显式特化都应作为常量初始化,以避免此类问题。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-05-16
  • 2015-05-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-10-22
相关资源
最近更新 更多