144

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?

Community
  • 1
  • 1
Igor
  • 26,650
  • 27
  • 89
  • 114

4 Answers4

177

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
};
Johannes Schaub - litb
  • 496,577
  • 130
  • 894
  • 1,212
  • Johannes, are templates filed under "types"? I thought they were what types can be made from, but not types themselves? – sbi Dec 27 '10 at 21:24
  • @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. – Johannes Schaub - litb Dec 27 '10 at 22:18
  • @JohannesSchaub-litb so there is no way to type template with let say std::string? like template 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? – relaxxx Mar 29 '12 at 09:26
  • 1
    I'd love to see this answer completed with template class member objects, i.e. template struct mystruct – Johnny Pauling Mar 02 '13 at 09:31
  • 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 – HEKTO Jun 06 '17 at 03:29
  • @HEKTO: which are the other errors in this answer ? – Destructor Jul 26 '17 at 09:03
  • @JohannesSchaub-litb: `template struct SillyExample { static void do_it() { A = 10; } }; // pass flag as argument int flag; SillyExample test;` This fails in compilation in both g++ & clang++. See live demo [here](https://wandbox.org/permlink/d0M7zFzd4BGGM3p3) . Correct your answer accordingly. – Destructor Jul 26 '17 at 09:04
  • Nice answer, I think It is worth to mention that in the non-type parameter category. The parameter name is optional, so `template` is a valid declaration – r0n9 Nov 16 '17 at 01:03
164

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
}
maxaposteriori
  • 7,267
  • 4
  • 28
  • 25
  • 12
    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 struct Factorial` can have `static constexpr int value = N * Factorial::value;` – bobobobo Aug 07 '17 at 22:08
  • 2
    @bobobobo this was answered before C++11 and `constexpr`. – Justin Meiners Nov 16 '18 at 22:25
  • I got error when using enum as parameter. . The possible value of enum is finite, is there any way to make it work. – iaomw Sep 06 '20 at 15:36
  • @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. – Kaihaku Sep 19 '21 at 15:16
17

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.
 }
Martin York
  • 257,169
  • 86
  • 333
  • 562
15

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.

akjoshi
  • 15,374
  • 13
  • 103
  • 121
Jonathan
  • 151
  • 1
  • 2
  • 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? – liakoyras Oct 26 '22 at 16:26