【发布时间】:2016-11-18 12:30:47
【问题描述】:
我有一个整数N,我在编译时就知道了。我还有一个 std::array 保存描述 N 维数组形状的整数。我想在编译时使用元编程技术生成嵌套循环,如下所述。
constexpr int N {4};
constexpr std::array<int, N> shape {{1,3,5,2}};
auto f = [/* accept object which uses coords */] (auto... coords) {
// do sth with coords
};
// This is what I want to generate.
for(int i = 0; i < shape[0]; i++) {
for(int j = 0; j < shape[1]; j++) {
for(int k = 0; k < shape[2]; k++) {
for(int l = 0; l < shape[3]; l++) {
f(i,j,k,l) // object is modified via the lambda function.
}
}
}
}
注意参数 N 在编译时是已知的,但在编译之间可能会发生不可预测的变化,因此我不能像上面那样对循环进行硬编码。理想情况下,循环生成机制将提供一个接口,该接口接受 lambda 函数,生成循环并调用生成上述等效代码的函数。我知道可以在运行时用一个 while 循环和一组索引编写一个等效循环,并且已经有了这个问题的答案。但是,我对这个解决方案不感兴趣。我也对涉及预处理器魔法的解决方案不感兴趣。
【问题讨论】:
-
你的编译器不是在优化开启时展开循环吗?
-
他很可能会展开一些循环,但这不是重点。我问的是循环生成而不是循环展开。无论如何,数组可以保存比上面指定的整数大得多的值。一般来说,完全展开循环是不可能的。
-
使用单循环并从单循环索引计算
i、j、k和l等。 -
@Cheersandhth.-Alf 在这种情况下,它没有展开。这里调用者创建了一个包装结构的实例,它在一个普通的运行时循环中调用下一个实例的成员函数,依此类推。因此,在实例化模板时,编译器不会展开循环。它相当递归地计算更深实例的模板参数。 Godboldt 的在线反汇编器证实了这一点:godbolt.org/g/4xGzoB。另一方面,如果您添加
-O2(甚至没有-O3!),所有计算都将在编译时执行,代码归结为单个std::cout.operator<<()调用。 -
@Sergey:我想为上面的评论发布具体代码(使用单个循环),它更简单,但我制作的
i变化最快,l最慢,并且我没有时间修复它。或者我觉得我没有。也许你可以为我修好并发布它?在coliru.stacked-crooked.com/a/e073a4a40bc7a15e
标签: c++ templates c++11 metaprogramming c++14