这对您来说可能为时已晚,但它可能对其他找到此 SO 的人有用,因此这是我的答案。
@Rapptz 的答案没有错,但您的问题的答案可能是“是”。是的,不能像讨论的那样使用 for 循环。但是,您想遍历循环,不必具有 for 循环的心态,例如使用 recursion 是可能的。它更丑陋,但对于一些用户来说,从运行时(不应该在运行时)到编译时获得一些繁重的东西可能是值得的。对于一些增加的丑陋可能不值得牺牲代码的清洁度,所以这取决于每个人来决定,我只是想证明这是可能的。对资源有严格限制的嵌入式域可能值得考虑。通过这种递归,您可以描述许多算法,您可能可以像 prolog 一样执行 tail + head 并一次处理一个条目,或者复制数组并在其中更改一次条目(可怜的以模板为中心的串联而不改变返回类型)。只是有一个限制,在编译时不会看到循环结束的“if”条件。所以典型的算法是这样的:
template<int input>
int factorialTemplateSimpler() {
if (input < 2) {
return 1;
} else {
return factorialTemplateSimpler<input-1>() * input;
}
}
不会编译,因为工具链会一直递减,直到它被终止(可能在 1000 次递归之后)。要让模板看到 结束状态,您必须像这样明确声明:
https://godbolt.org/z/d4aKMjqx3
template<int N>
constexpr int foo() {
return N;
}
const int maxLoopIndex = 10;
template<int loopIndex>
constexpr int getSum() {
return foo<loopIndex>() + getSum<loopIndex + 1>();
}
template<>
constexpr int getSum<maxLoopIndex>() {
return 0;
}
int main() {
constexpr auto sum = getSum<0>();
return sum;
}
这是你想要的,它用 C++14 编译,不知道为什么它也用 C++11 编译。
许多算法都有一个特殊情况来做一些与典型算法不同的事情,你必须为它做一个单独的实现(因为 if 不会在实例化)。而且你还必须在某个地方结束循环,所以你必须实现单独的exit case。为了节省您额外的输入,最好将 exit case 和 special case 放在同一个索引中,这样您就不必创建重复的实现并增加维护.因此,您必须决定以递增或递减方式计数是否更好。因此,您将只需要执行一次组合的特殊情况和退出情况。把它放到一个上下文中,对于像阶乘这样的东西,我会递减,所以循环的 0-case 和结束将用相同的代码实现,然后让算法在从深度递归返回时完成工作。
如果您没有任何特殊情况并且您必须制作一种特殊情况,例如在上面的代码中,我知道返回 0 是安全的,并且我知道您何时数到 10 但不包括它,因此我在索引 10 处做了特例并返回 0。
template<>
constexpr int getSum<maxLoopIndex>() {
return 0;
}
如果您无法使用该技巧,那么您必须实现算法的一个子集(没有递归),但在索引 9 处停止,如下所示:
template<>
constexpr int getSum<maxLoopIndex-1>() {
return foo<maxLoopIndex-1>();
}
注意:你可以使用 const 变量和方程,只要它们在编译时。
完整示例:
https://godbolt.org/z/eMc93MvW8
template<int N>
constexpr int foo() {
return N;
}
const int maxLoopIndex = 10;
template<int loopIndex>
constexpr int getSum() {
return foo<loopIndex>() + getSum<loopIndex + 1>();
}
template<>
constexpr int getSum<maxLoopIndex-1>() {
return foo<maxLoopIndex-1>();
}
int main() {
constexpr auto sum = getSum<0>();
return sum;
}
这是一个递减的例子,让你更轻松(最终情况为 0):
https://godbolt.org/z/xfzcGMrcq
template<int N>
constexpr int foo() {
return N;
}
template<int index>
constexpr int getSum() {
return foo<index>() + getSum<index-1>();
}
template<>
constexpr int getSum<0>() {
return foo<0>();
}
int main() {
constexpr auto sum = getSum<10 - 1>(); // loop 0..9
return sum;
}
如果您启用 C++20,那么我什至可以在编译时填充一个查找表,其中包含指向您的 foo 实例的函数指针,只是为了证明在编译时可以完成很多可能性。完整示例:
https://godbolt.org/z/c3febn36v
template<int N>
constexpr int foo() {
return N;
}
const int lookupTableSize = 10;
template <int lookupIndex>
constexpr std::array<int(*)(), lookupTableSize> populateLookupTable() {
auto result = populateLookupTable<lookupIndex - 1>();
result[lookupIndex] = foo<lookupIndex>;
return result;
}
template <>
constexpr std::array<int(*)(), lookupTableSize> populateLookupTable<0>() {
std::array<int(*)(), lookupTableSize> lookupTable;
lookupTable[0] = foo<0>;
return lookupTable;
}
const auto lookupTable = populateLookupTable<lookupTableSize - 1>();
int main() {
return lookupTable[2]();
}
这里的另一个例子是填充 cos 查找表:
https://godbolt.org/z/ETPvx4nex
#include <cmath>
#include <array>
#include <cstdint>
#include <algorithm>
const int lookupTableSize = 32;
template <int lookupIndex>
constexpr std::array<int8_t, lookupTableSize> populateLookupTable() {
auto previousResult = populateLookupTable<lookupIndex + 1>();
auto pi = acosf(-1);
auto inRadians = (((float)lookupIndex)/lookupTableSize) * 2 * pi;
previousResult[lookupIndex] = 127 * std::clamp(cosf(inRadians), -1.0f, 1.0f);
return previousResult;
}
template <>
constexpr std::array<int8_t, lookupTableSize> populateLookupTable<lookupTableSize>() {
return { 0 };
}
const auto lookupTable = populateLookupTable<0>();
int main() {
return lookupTable[2];
}