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)