编译时内的模板参数展开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 循环的,它实际上不需要编译时展开。如果&lt;i&gt; 的所有值都需要SubDomain&lt;i&gt;,那么编译器自然必须实例化所有这些值。这是不可避免的工作。 @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&lt;i-1&gt;();

2017 注意:您现在可以使用编译时if constexpr(i!=0) createSubDomains&lt;i-1&gt;();

【讨论】:

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。

另请注意,循环展开也适用于正常循环,作为编译器优化的一部分。不限于templates。

【讨论】:

您可能第一次就做对了...您的演示与@Jayesh 给出的示例不同。在您的代码中,您使用N 实例化A,这确实是一个编译时间常数。在 OP 的示例中,他尝试使用 i 实例化模板,这是一个常规变量。这是行不通的,因为编译器无法在编译时实例化类型未知的类。所以OP的代码是无效的,不会编译。 @eran,感谢您指出错误。我已经重新修改了答案。 谢谢....我现在实际上并没有尝试使用它,但现在我尝试了..它也给了我一个错误...... 请参考我编辑过的问题的最后一行....有没有在不使用动态多态性的情况下实现我想要的东西?一切在编译时就知道了……但在编写代码时就不知道了…… @Jayesh,不幸的是有办法。因为max_subdomain 可以是任何常量,我们不能基于它声明对象。但是,如果您可以在一个新问题中描述您声明此类对象的确切目标是什么,那么您可能会得到很好的帮助来找到更好的方法。

以上是关于编译时内的模板参数展开for循环?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 C++ 中展开嵌套的 for 循环?

优化编译器如何决定何时展开循环以及展开循环的程度?

循环展开与循环平铺

在 MSVC C++ 中强制循环展开

带有 Shader Model 2.0 的 HLSL 循环:错误 X3511 无法展开循环

GCC - 仅在特定功能上启用编译器标志