template arguments inside a compile time unrolled for loop?

一笑奈何 提交于 2019-12-03 14:48:28

Re-Edit:

My previous answer was correct. I have tried your code, it's giving compiler error. You cannot declare objects like that, as i cannot remain a compile time constant (as you are intending to do i++). template parameter must always be compile time constants. Here is the demo.

Also note that, loop unrolling is done for normal loops also, as part of optimization by compilers. It's not limited to templates.

There's a stadard solution for this. Convert iteration into recursion.

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.
}

Note: you can't use a runtime if(i!=0) createSubDomains<i-1>();.

2017 Note: you can now use a compile-time if constexpr(i!=0) createSubDomains<i-1>();

Even though you already accepted @iammilind 's answer, let me propose another one, because his reasoning for why i is not compile-time-constant is was not correct.

Suppose you have

    template<unsigned int MAX> struct SubDomain {...};
    ...

and you want to declare an instance of it ...

    SubDomain<i> tmp(member);

then i must be a commonly so-called compile-time-constant. What is that?


Pedantry

The standard assigns the term nontype template argument to template arguments that are not types (D'Oh).

14.3.2 Template non-type arguments [temp.arg.nontype]

A template-argument for a non-type, non-template template-parameter shall be one of:
— an integral constant-expression of integral or enumeration type; or
— ... [more follow, but are not relevant]

Right the first point contains a reference for further research: an integral constant-expression. This leads us to

5.19 Constant expressions [expr.const]

In several places, C + + requires expressions that evaluate to an integral or enumeration constant: 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), as static member initializers (9.4.2), and as integral or enumeration non-type template arguments (14.3).

Then, the key is:

An integral constant-expression can involve only literals (2.13), enumerators, const variables or static data members of integral or enumeration types initialized with constant expressions (8.5), non-type template parameters of integral or enumeration types, and sizeof expressions.


Pedantry application

If we look back at your loop:

for (int i=...
    ...
    SubDomain<i>

then we can now observe that i is not allowed there. Why? Because i is NOT a const variable.

The observing reader might now think you can circumvent this:

for (int i=...
        ...
        const int I = i;
        SubDomain<I>

But is this really allowed? Negative, I = i is not an integral constant-expression, because i is not. It helps to realise that the rule for integral constant-expressions is applied recursively.

E.g., the following is valid code:

template <int> void foo() {}     
int main () {
    const int ccI = 0;
    const int ccJ = ccI*2;
    const int ccK = ccJ/4;     
    foo<ccK>();
}

But if make just one part of the chain non-const, then ccK is not considered integral constant anymore:

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
}


Summary

So, in human readable form, template arguments that are not types, but (integer) values, must be compile-time-constant:

  • the initializer of a compile-time-constant must only involve other compile-time-constants
  • a literal value is a compile-time-constant
  • an enum value is a compile-time-constant
  • function-calls don't give compile-time-constants (for some advanced reasons)
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!