views:

2408

answers:

3

If you are using a template in C++ that takes an integer value as a parameter, are there any requirements on an integer variable used as the parameter that are different than if the variable was used as a parameter in a function call?

This is a follow-up to question here . I specifically want to address if there is a difference WRT variables declared as "extern const int" for functions or templates?

I can see that for some template cases the parameter value would be needed at compile time. Is this always true? Is there a way to specify, maybe for only certain uses of the parameter value, that the value be used at runtime?

+2  A: 

It is always the case that the value of the int is needed at compile time.

Since each template instantiation is a separate piece of compiled code (even for integer template paramaters) that integer needs to be available when compiled (and it must be guaranteed to never change).

This is also why it's a good idea to not use integer template parameters when you are going to be using a large number of unique values - you can quickly end up with a huge executable file.

Eclipse
+9  A: 

The following is from the standard.

14.3.2.1:

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
  • the name of a non-type template-parameter; or
  • the address of an object or function with external linkage, including function templates and function template-ids but excluding non-static class members, expressed as & id-expression where the & is optional if the name refers to a function or array, or if the corresponding template-parameter is a reference; or
  • a pointer to member expressed as described in 5.3.1 .

5.19.1:

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

 constant-expression:
            conditional-expression

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. Floating literals (2.13.3) can appear only if they are cast to integral or enumeration types. Only type conversions to integral or enumera- tion types can be used. In particular, except in sizeof expressions, functions, class objects, pointers, or references shall not be used, and assignment, increment, decrement, function-call, or comma operators shall not be used.

With respect to your previous post I believe the essence in the part "const variables ... initialised with ..." (and I don't think initialised externally counts).

mweerden
+3  A: 

It has to be a integral constant expression. That's explained by the Standard document at 5.19:

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. Floating literals (2.13.3) can appear only if they are cast to integral or enumeration types. Only type conversions to integral or enumeration types can be used.

Note that "integral" is another term for "integer", but is not the same as "int". "char" for example has integer/integral type, but is not the int type, obviously. So concretely, the following is allowed

  • 10 or 10L or anything like that
  • enum { THIS, OR, THAT };
  • int const this_one = 10;
  • sizeof(char)
  • of course, any other template parameter as detailed above

Any of those can be used as a template argument for a parameter that has an integral type of the corresponding type. Some conversions are still applied though. So if it wants an int and you pass a char, it automatically promotes the char to the int. Same if you provide an enumerator and it wants an int.

So by those rules, if you have

extern const int SomeName;

And it does not see a definition which initializes that constant with a integral constant expression, it can't be used as a template argument. But it can be used as a function argument, of course. Those don't need to be known at compile time because those are not part of a type. The moment you name a template specialization, the arguments you used become part of the type:

MyGreatStack<int, 4> // 4 is now part of the type MyGreatStack<int, 4>!

Note that there are other ways to pass SomeName as an argument. However, all of which can not be accepted by an integer template parameter. You can accept the above by a reference parameter, for example

template<const int& V> struct NowItWorks { };

And it would accept the SomeName of above. Now, rather than a value, a particular location that is unique across the whole program (as the variable has extern linkage) has been chosen.

Johannes Schaub - litb