非 constexpr 变量模板是否具有零开销?

Do non-constexpr variable templates have zero overhead?

我的意思是,当你实例化一个不是constexpr的模板变量时,究竟会生成什么?

考虑一个计算阶乘的基本变量模板:

template<int N>
int fat = N*(fat<N-1>);

template<>
int fat<0> = 1;

int main() {
    return fat<5>;
}

我的直觉是它会生成这样的东西:

int fat0 = 1;
int fat1 = 1*fat0;
int fat2 = 2*fat1;
int fat3 = 3*fat2;
int fat4 = 4*fat3;
int fat5 = 5*fat4;

int main() {
    return fat5;
}

我试图在 C++ Insights 上查看它,但生成的代码如下所示:

template<int N>
const int fat = N*(fat<N-1>);

template<>
const int fat<0> = 1;

int main()
{
  return fat<5>;
}

...这根本没有帮助。

我的下一次尝试是查看使用 godbolt.org 生成的(优化的)程序集,看看是否有任何区别:

令我惊讶的是,!模板版本的汇编行数大约是手写版本的两倍。 GCC 似乎为每个实例化生成一个额外的 "guard variable" 。 Clang 也这样做。

现在,考虑到零开销原则,这些变量应该做一些重要的事情。具体来说,我在编写 "unrolled" 版本时遗漏了一些东西。这是什么 "something" 我错过了什么?

P.S.: 为了进一步伤害我的大脑,MSVC 采用相反的方式,为模板版本生成的程序集实际上 3 倍小 比没有模板的版本。不过,我无法从生成的程序集中理解很多,所以我将其排除在主要问题之外。

编译器完全按照您的要求进行操作。变量 fat 作为外部链接,因此每个实例化都必须可以被动态链接到这个程序的任何其他程序访问。所以编译器必须生成代码。

但是如果你声明它 static 优化器可以 remove 额外的实例化:

template<int N>
static int fat = N*(fat<N-1>);

template<>
int fat<0> = 1;

int main() {
    return fat<5>;
}

注意:此代码可以用作为什么 Clang 不应用于 c++ 的示例。 clang 模板堆栈搞砸了,Clang return 0.!!

自 2016 年以来报告的 Clang 错误:bug 29033