在这种情况下,为什么模板即时深度超过限制?

Why does the template instantion depth exceed the limit in this case?

本文关键字:深度 这种情况下 为什么      更新时间:2023-10-16

我在C++中输入此代码:

#include <iostream>
template <int n, int i = 0>
class show {
public:
show () {
if (i % 2) {
std::cout << i << std::endl;
show <n-1, i+1>();
}else {
show <n,i+1>();
}
}
};
template <int i>
class show <0, i> {};

int main()
{
show <6>();
}

我以为它会写 6 个不能被 2 整除的前几个数字。相反,我收到一个错误

致命错误:模板即时超过最大值 100

如果它最多应该实例化 12 个实例,为什么会出现此错误?

尝试使用

show () {
if ( i % 2 )
std::cout << i << std::endl;
show<n-(i%2 ? 1 : 0), i+1>();
}

或者,如果您可以使用 C++17,也可以使用

show () {
if constexpr (i % 2) {
std::cout << i << std::endl;
show <n-1, i+1>();
}else {
show <n,i+1>();
}
}

代码的问题

show () {
if (i % 2) {
std::cout << i << std::endl;
show <n-1, i+1>();
}else {
show <n,i+1>();
}
}

i % 2是真还是假并不重要:showS、show<n-1, i+1>show<n, i+1>都实现了。因此,实现了许多不必要的show,并达到了模板实例化的最大数量。

C++17 引入if constexpr正是为了避免此类问题。

在 C++17 之前,你可以用

show<n-(i%2 ? 1 : 0), i+1>();

在函数show()中:

show () {
if (i % 2) {
std::cout << i << std::endl;
show <n-1, i+1>();
}else {
show <n,i+1>();
}
}

类模板show在子句的两个部分中实例化if

您需要切换到if constexpr才能执行单个正确的实例:

show () {
if constexpr (i % 2) {
std::cout << i << std::endl;
show <n-1, i+1>();
}else {
show <n,i+1>();
}
}

为了了解发生了什么,让我们尝试"内联"更简单表达式的实例化show<1>()

在第一个递归级别,我们将得到:

if (0 % 2) {
std::cout << 0 << std::endl;
show <0,1>(); // <-- this will instantiate to a no-op.
} else {
show <1,1>();
}

这是第二个:

if (0 % 2) {
std::cout << 0 << std::endl;
} else {
show <1,1>(); // <-- now this will instantiate
}

第三:

if (0 % 2) {
std::cout << 0 << std::endl;
} else {
if (1 % 2) {
std::cout << 1 << std::endl;
show <0,1>(); // <-- this will instantiate to a no-op.
} else {
show <1,2>();
}
}

第四:

if (0 % 2) {
std::cout << 0 << std::endl;
} else {
if (1 % 2) {
std::cout << 1 << std::endl;
} else {
show <1,2>(); // <-- now this will instantiate
}
}

第五:

if (0 % 2) {
std::cout << 0 << std::endl;
} else {
if (1 % 2) {
std::cout << 1 << std::endl;
} else {
if (1 % 2) {
std::cout << 2 << std::endl;
show <0,2>(); // <-- this will again instantiate to a no-op...
} else {
show <1,3>(); // <-- ... and I'm sure you can see where this is going.
}
}
}

这说明了其他答案已经指出的内容:模板是代码生成器。这意味着,如果编译器到达模板,它将始终从该模板生成代码,而不管程序在到达该模板之前具有任何逻辑。

这就是if constexpr阻止的。如果if constexpr处的表达式计算结果为 falsy 值,编译器将忽略所述if constexpr块内的任何代码。

如果您没有 C++17 编译器,则可以使用 SFINAE 获得相同的结果。