156

When declaring a template, I am used to having this kind of code:

template <class T> 

But in this question, they used:

template <unsigned int N> 

I checked that it compiles. But what does it mean? Is it a non-type parameter? And if so, how can we have a template without any type parameter?

1
  • 3
    I am disappointed that most tutorials do not specify this usage. Commented May 23, 2023 at 16:53

5 Answers 5

187

Yes, it is a non-type parameter. You can have several kinds of template parameters

  • Type Parameters.
    • Types
    • Templates (only classes and alias templates, no functions or variable templates)
  • Non-type Parameters
    • Pointers
    • References
    • Integral constant expressions

What you have there is of the last kind. It's a compile time constant (so-called constant expression) and is of type integer or enumeration. After looking it up in the standard, i had to move class templates up into the types section - even though templates are not types. But they are called type-parameters for the purpose of describing those kinds nonetheless. You can have pointers (and also member pointers) and references to objects/functions that have external linkage (those that can be linked to from other object files and whose address is unique in the entire program). Examples:

Template type parameter:

template<typename T> struct Container { T t; }; // pass type "long" as argument. Container<long> test; 

Template integer parameter:

template<unsigned int S> struct Vector { unsigned char bytes[S]; }; // pass 3 as argument. Vector<3> test; 

Template pointer parameter (passing a pointer to a function)

template<void (*F)()> struct FunctionWrapper { static void call_it() { F(); } }; // pass address of function do_it as argument. void do_it() { } FunctionWrapper<&do_it> test; 

Template reference parameter (passing an integer)

template<int &A> struct SillyExample { static void do_it() { A = 10; } }; // pass flag as argument int flag; SillyExample<flag> test; 

Template template parameter.

template<template<typename T> class AllocatePolicy> struct Pool { void allocate(size_t n) { int *p = AllocatePolicy<int>::allocate(n); } }; // pass the template "allocator" as argument. template<typename T> struct allocator { static T * allocate(size_t n) { return 0; } }; Pool<allocator> test; 

A template without any parameters is not possible. But a template without any explicit argument is possible - it has default arguments:

template<unsigned int SIZE = 3> struct Vector { unsigned char buffer[SIZE]; }; Vector<> test; 

Syntactically, template<> is reserved to mark an explicit template specialization, instead of a template without parameters:

template<> struct Vector<3> { // alternative definition for SIZE == 3 }; 
Sign up to request clarification or add additional context in comments.

8 Comments

Johannes, are templates filed under "types"? I thought they were what types can be made from, but not types themselves?
@sbi see the explanation: "After looking it up in the standard, i had to move class templates up into the types section - even though templates are not types. But they are called type-parameters for the purpose of describing those kinds nonetheless.". Footnote 126 on 14.1/2 says so. It's just a classification made to make non-type parameters something that declares a value/reference and type-parameters be something declaring a type name or template name.
@JohannesSchaub-litb so there is no way to type template with let say std::string? like template<std::string S> class with some static counter in it to create unique id for every different string? hashing string to int would be the only way unfortunately right?
I'd love to see this answer completed with template class member objects, i.e. template<typename C, typename R, typename P1, typename P2> struct mystruct<R(C::*)(P1,P2)>
The piece of code with SillyExample can't be compiled by GCC 4.8.4. The first error is the value of ‘flag’ is not usable in a constant expression. There are other errors as well
|
170

It's perfectly possible to template a class on an integer rather than a type. We can assign the templated value to a variable, or otherwise manipulate it in a way we might with any other integer literal:

unsigned int x = N; 

In fact, we can create algorithms which evaluate at compile time (from Wikipedia):

template <int N> struct Factorial { enum { value = N * Factorial<N - 1>::value }; }; template <> struct Factorial<0> { enum { value = 1 }; }; // Factorial<4>::value == 24 // Factorial<0>::value == 1 void foo() { int x = Factorial<4>::value; // == 24 int y = Factorial<0>::value; // == 1 } 

4 Comments

You can also use type static constexpr int instead of your enum. So the Factorial<0> template would have static constexpr int value = 1, and template <int N> struct Factorial can have static constexpr int value = N * Factorial<N - 1>::value;
@bobobobo this was answered before C++11 and constexpr.
I got error when using enum as parameter. <Non-type template argument is not a constant expression>. The possible value of enum is finite, is there any way to make it work.
@JustinMeiners same reasoning doesn't get applied if you post a "duplicate question" expecting to get an answer not from 30 years ago... And that comment is useful to me.
19

You templatize your class based on an 'unsigned int'.

Example:

template <unsigned int N> class MyArray { public: private: double data[N]; // Use N as the size of the array }; int main() { MyArray<2> a1; MyArray<2> a2; MyArray<4> b1; a1 = a2; // OK The arrays are the same size. a1 = b1; // FAIL because the size of the array is part of the // template and thus the type, a1 and b1 are different types. // Thus this is a COMPILE time failure. } 

Comments

17

A template class is like a macro, only a whole lot less evil.

Think of a template as a macro. The parameters to the template get substituted into a class (or function) definition, when you define a class (or function) using a template.

The difference is that the parameters have "types" and values passed are checked during compilation, like parameters to functions. The types valid are your regular C++ types, like int and char. When you instantiate a template class, you pass a value of the type you specified, and in a new copy of the template class definition this value gets substituted in wherever the parameter name was in the original definition. Just like a macro.

You can also use the "class" or "typename" types for parameters (they're really the same). With a parameter of one of these types, you may pass a type name instead of a value. Just like before, everywhere the parameter name was in the template class definition, as soon as you create a new instance, becomes whatever type you pass. This is the most common use for a template class; Everybody that knows anything about C++ templates knows how to do this.

Consider this template class example code:

#include <cstdio> template <int I> class foo { void print() { printf("%i", I); } }; int main() { foo<26> f; f.print(); return 0; }

It's functionally the same as this macro-using code:

#include <cstdio> #define MAKE_A_FOO(I) class foo_##I \ { \ void print() \ { \ printf("%i", I); \ } \ }; MAKE_A_FOO(26) int main() { foo_26 f; f.print(); return 0; }

Of course, the template version is a billion times safer and more flexible.

1 Comment

I am very late to the discussion, but what this the advantage of using this method compared to initialization via a constructor? Is it because the initialization via a constructor (foo f = foo(26)) happens in runtime while the template method happens in compile time?
5

Yes, C++ template can include a numeric value as the template parameter, as simply as mytype<1> and mytype<2>.

  1. As class template, std::array<class, size_t> from the C++ header <array> is the perfect example of using a value as the template, along with a typename. Its usage is std::array<int, 5>. ( Read more about std::array: https://cplusplus.com/reference/array/array ,
    or https://en.cppreference.com/w/cpp/container/array )

  2. In General, in C++ language which is a strict type-specific language, the use of such template with different parameters is automatically implemented as each different type. std::array<int, 5> and std::array<int, 6> and std::array<long long, 5> and std::array<long long, 6> are all explicitly different types, fixed at compile-time. They are interpreted as different as mytypeA, mytypeB, mytypeC, and mytypeD. Those classes prepare their member variables of different types and sizes. Thus they are not compatible, and the compiler will ring an alarm with an error if you place these class instances at each other's spot, unless 'conversion operator function' is implemented inside. Using this feature, you can purposely prohibit the type conversion between similar classes, by assigning them different values at template parameter.

  3. As function template, we can handle the compile-time fixed array of every possible size, for example.

  4. Not limited to those above, there are so many other possibilities. For example, as you wonder the utility and worthiness of template with only numeric values, we can implement matrix(math term of number array) as a type by combining class template and function template. We don't need the runtime pointer calculation or std::vector container!

    This is just a sketch of conceptual code.

     template <size_t ROW, size_t COL> class matrix { int64_t data[ROW][COL]; matrix() { /* constructor */ } matrix(int64_t** pointer_of_numeric_array) { /* constructor */ } matrix(int64_t[ROW][COL]& assigning_data) { /* constructor */ } }; template <size_t leftR, size_t leftC, size_t rightR, size_t rightC> matrix<leftR, rightC> operator* // look at the return type! (const matrix<leftR, leftC>& mat1, const matrix<rightR, rightC>& mat2) { // so that we can control the relation of type parameters! matrix<leftR, rightC> result; /* do the math here */ return result; } // later: matrix<3, 5> mydata1; matrix<5, 7> mydata2; auto result = mydata1 * mydata2; 

    the type of result will be matrix<3, 7>, a new and different type.

After all, template made them possible by writing only one efficient code!

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.