Что означает шаблон <unsigned int N>?

При объявлении шаблона я использую такой код:

template <class T>

Но в этом вопросе они использовали:

template <unsigned int N>

Я проверил, что он компилируется. Но что это значит? Является ли это непиговым параметром? И если да, то как мы можем создать шаблон без какого-либо параметра типа?

Ответ 1

Совершенно возможно создать шаблон класса по целому, а не по типу. Мы можем назначить шаблонное значение переменной или иным образом манипулировать ею так, как мы могли бы с любым другим целым литералом:

unsigned int x = N;

Фактически мы можем создавать алгоритмы, которые оцениваются во время компиляции (из 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
}

Ответ 2

Да, это параметр не-типа. Вы можете иметь несколько типов параметров шаблона

  • Введите параметры.
    • Типы
    • Шаблоны (только классы и шаблоны псевдонимов, без функций или шаблонов переменных)
  • Неигровые параметры
    • Указатели
    • Ссылки
    • Интегральные константные выражения

У вас есть последний вид. Это константа времени компиляции (так называемое постоянное выражение) и имеет тип integer или перечисление. После поиска в стандарте мне пришлось переместить классные шаблоны в раздел типов - хотя шаблоны не являются типами. Но они называются параметрами типа с целью описания тех видов. У вас могут быть указатели (а также указатели элементов) и ссылки на объекты/функции, которые имеют внешнюю привязку (те, которые могут быть связаны с другими объектными файлами и адрес которых уникален во всей программе). Примеры:

Параметр типа шаблона:

template<typename T>
struct Container {
    T t;
};

// pass type "long" as argument.
Container<long> test;

Параметр целочисленного шаблона:

template<unsigned int S>
struct Vector {
    unsigned char bytes[S];
};

// pass 3 as argument.
Vector<3> test;

Параметр указателя шаблона (передача указателя на функцию)

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<int &A>
struct SillyExample {
    static void do_it() { A = 10; }
};

// pass flag as argument
int flag;
SillyExample<flag> test;

Параметр шаблона шаблона.

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;

Шаблон без каких-либо параметров невозможен. Но шаблон без явного аргумента возможен - он имеет аргументы по умолчанию:

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

Vector<> test;

Синтаксически template<> зарезервировано для обозначения явной специализации шаблона вместо шаблона без параметров:

template<>
struct Vector<3> {
    // alternative definition for SIZE == 3
};

Ответ 3

Вы templatize своего класса на основе "unsigned int".

Пример:

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

Ответ 4

Класс шаблона похож на макрос, только намного меньше зла.

Подумайте о шаблоне как макросе. Параметры шаблона заменяются на определение класса (или функции) при определении класса (или функции) с использованием шаблона.

Отличие состоит в том, что параметры имеют "типы" и переданные значения проверяются во время компиляции, как параметры для функций. Допустимыми типами являются ваши обычные типы С++, такие как int и char. Когда вы создаете экземпляр класса шаблона, вы передаете значение указанного вами типа, а в новой копии определения класса шаблона это значение заменяется там, где имя параметра находилось в исходном определении. Также как макрос.

Вы также можете использовать типы "class" или "typename" для параметров (они действительно одинаковы). С параметром одного из этих типов вы можете передать имя типа вместо значения. Как и раньше, везде имя параметра находилось в определении класса шаблона, как только вы создаете новый экземпляр, становится тем типом, который вы передаете. Это наиболее распространенное использование для класса шаблона; Все, кто что-то знает о шаблонах на С++, знают, как это сделать.

Рассмотрим этот пример кода примера шаблона:

#include <cstdio>
template <int I>
class foo
{
  void print()
  {
    printf("%i", I);
  }
};

int main()
{
  foo<26> f;
  f.print();
  return 0;
}

Он функционально совпадает с этим макрокомандом:

#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;
}

Конечно, версия шаблона в миллиард раз безопаснее и гибче.