编译时内的模板参数展开for循环?
Posted
技术标签:
【中文标题】编译时内的模板参数展开for循环?【英文标题】:template arguments inside a compile time unrolled for loop? 【发布时间】:2011-10-08 05:09:36 【问题描述】:wikipedia (here) 给出了 for 循环的编译时间展开...... 我想知道我们是否可以在里面使用带有模板语句的类似 for 循环... 比如……
以下循环是否有效
template<int max_subdomain>
void Device<max_sudomain>::createSubDomains()
for(int i=0; i< max_subdomain; ++i)
SubDomain<i> tmp(member);
...
// some operations on tmp
...
SubDomain 是一个接收模板参数 int 的类,这里使用的是 Device 类成员的参数构造。
谢谢大家的回答... 现在你知道我想要什么了... 有没有我想达到的目标??
我终于得到了我想要的............ 而不是直接使用for循环... 可以改用Boost::MPL for_each construct。我还没有实现它,但我猜这提供了一种方法来做我想做的事.....
我从另一个堆栈溢出问题here 中得到了答案...但是同一问题的comment 拒绝使用它,因为它会非常慢(当然对于大型 for 循环)...但是..对于不大的循环,我认为不应该有任何腹胀......我会尝试代码并让你知道结果......
example 中很好地说明了用法
【问题讨论】:
你试过了吗?你说的有效是什么意思?问题是什么? 其实……现在我的代码里的东西很乱…… @Jayesh,请参考编辑后的答案。我在演示代码中犯了一个错误。 “谴责它的使用”注释是特定于 2D 循环的,它实际上不需要编译时展开。如果<i>
的所有值都需要SubDomain<i>
,那么编译器自然必须实例化所有这些值。这是不可避免的工作。
@MSalters:感谢您的澄清......
【参考方案1】:
对此有一个标准的解决方案。将迭代转换为递归。
template<int i>
void Device::createSubDomains()
SubDomain<i> tmp(member);
// some operations on tmp
createSubDomains<i-1>();
template<>
void Device<-1>::createSubDomains()
// End of recursion.
注意:您不能使用运行时if(i!=0) createSubDomains<i-1>();
。
2017 注意:您现在可以使用编译时if constexpr(i!=0) createSubDomains<i-1>();
【讨论】:
yaa.. 我知道...它也可以工作....但是当我在 wiki 中看到 for 循环示例时,我不得不问........谢谢无论如何答案......我是否会使用它取决于课堂上的其他一些实现......我将不得不尝试一下,看看......会让你知道我选择哪一个以及为什么...... .【参考方案2】:即使你已经接受了@iammilind 的回答,让我再提出一个,因为他关于为什么i
不是编译时常数is 的推理不正确。
假设你有
template<unsigned int MAX> struct SubDomain ...;
...
你想声明它的一个实例......
SubDomain<i> tmp(member);
那么i
必须是通常所谓的compile-time-constant。那是什么?
学究
标准将术语 nontype template argument
分配给不是类型的模板参数 (D'Oh)。
14.3.2 模板非类型参数 [temp.arg.nontype]
非类型、非模板模板参数的模板参数应为以下之一: — 整数或枚举类型的整数常量表达式;或 — ... [更多关注,但不相关]
正确的第一点包含进一步研究的参考:an integral constant-expression
。这导致我们
5.19 常量表达式 [expr.const]
在几个地方,C++ 需要计算结果为整数或枚举常量的表达式:as array bounds (8.3.4, 5.3.4), as case expressions (6.4.2), as bit-field lengths (9.6), as enumerator initializers (7.2), 作为静态成员初始化器 (9.4.2)、和作为整数或枚举非类型模板参数 (14.3)。
那么,关键是:
整型常量表达式只能包含文字 (2.13)、枚举器、常量变量或静态 用常量表达式(8.5)初始化的整数或枚举类型的数据成员,非类型模板 整数或枚举类型的参数,以及表达式的大小。
学究式应用
如果我们回顾你的循环:
for (int i=...
...
SubDomain<i>
那么我们现在可以观察到i
是不允许的。为什么?因为i
不是const variable
。
观察者现在可能认为你可以绕过这个:
for (int i=...
...
const int I = i;
SubDomain<I>
但这真的允许吗?否定的,I = i
不是一个整数常量表达式,因为i
不是。它有助于认识到整数常量表达式的规则是递归应用的。
例如,以下是有效代码:
template <int> void foo()
int main ()
const int ccI = 0;
const int ccJ = ccI*2;
const int ccK = ccJ/4;
foo<ccK>();
但如果只将链的一部分设为非常量,则 ccK
不再被视为整数常量:
template <int> void foo()
int main ()
int ccI = 0;
const int ccJ = ccI*2; // not compile time constant
const int ccK = ccJ/4; // same
foo<ccK>(); // error
总结
因此,以人类可读的形式,不是类型而是(整数)值的模板参数必须是编译时常量:
编译时常量的初始化器必须只涉及其他编译时常量 文字值是编译时常量 枚举值是编译时常量 函数调用不提供编译时常量(出于某些高级原因)【讨论】:
【参考方案3】:重新编辑:
我之前的回答是正确的。我试过你的代码,它给出了编译器错误。你不能这样声明对象,因为i
不能保持编译时间常数(正如你打算做的那样 i++
)。 template
参数必须始终是编译时常量。这里是the demo。
另请注意,循环展开也适用于正常循环,作为编译器优化的一部分。不限于template
s。
【讨论】:
您可能第一次就做对了...您的演示与@Jayesh 给出的示例不同。在您的代码中,您使用N
实例化A
,这确实是一个编译时间常数。在 OP 的示例中,他尝试使用 i
实例化模板,这是一个常规变量。这是行不通的,因为编译器无法在编译时实例化类型未知的类。所以OP的代码是无效的,不会编译。
@eran,感谢您指出错误。我已经重新修改了答案。
谢谢....我现在实际上并没有尝试使用它,但现在我尝试了..它也给了我一个错误......
请参考我编辑过的问题的最后一行....有没有在不使用动态多态性的情况下实现我想要的东西?一切在编译时就知道了……但在编写代码时就不知道了……
@Jayesh,不幸的是有办法。因为max_subdomain
可以是任何常量,我们不能基于它声明对象。但是,如果您可以在一个新问题中描述您声明此类对象的确切目标是什么,那么您可能会得到很好的帮助来找到更好的方法。以上是关于编译时内的模板参数展开for循环?的主要内容,如果未能解决你的问题,请参考以下文章