Что означает template <unsigned int N>?

121

При объявлении шаблона я привык иметь такой код:

template <class T>

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

template <unsigned int N>

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

Игорь Окс
источник

Ответы:

148

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

unsigned int x = N;

Фактически, мы можем создавать алгоритмы, которые оцениваются во время компиляции (из Википедии ):

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
источник
11
Вы также можете использовать type static constexpr intвместо вашего enum. Так что в Factorial<0>шаблоне будет static constexpr int value = 1и template <int N> struct Factorialможет бытьstatic constexpr int value = N * Factorial<N - 1>::value;
bobobobo
@bobobobo на это ответили до C ++ 11 и constexpr.
Джастин Майнерс
154

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

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

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

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

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
};
Йоханнес Шауб - litb
источник
Йоханнес, шаблоны разделены на «типы»? Я думал, из каких типов можно сделать, но не из самих типов?
sbi
@sbi см. объяснение: «После поиска в стандарте мне пришлось переместить шаблоны классов вверх в раздел типов - хотя шаблоны не являются типами. Тем не менее, они называются параметрами типа для описания этих типов. ». Об этом говорится в сноске 126 к 14.1 / 2. Это просто классификация, сделанная для того, чтобы сделать параметры, не являющиеся типами, чем-то, что объявляет значение / ссылку, а параметры типа - тем, что объявляет имя типа или имя шаблона.
Йоханнес Шауб - лит,
@ JohannesSchaub-litb, поэтому нет возможности ввести шаблон, скажем, std :: string? как шаблон <std :: string S> с некоторым статическим счетчиком в нем для создания уникального идентификатора для каждой строки? Хеширование строки в int было бы, к сожалению, единственным способом?
relaxxx
1
Я бы хотел, чтобы этот ответ завершился объектами-членами класса шаблона, то есть template <typename C, typename R, typename P1, typename P2> struct mystruct <R (C :: *) (P1, P2)>
Джонни Полинг
Часть кода с SillyExampleне может быть скомпилирована GCC 4.8.4. Первая ошибка the value of ‘flag’ is not usable in a constant expression. Есть и другие ошибки
HEKTO 06
17

Вы шаблонизируете свой класс на основе 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.
 }
Мартин Йорк
источник
15

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

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

Разница в том, что параметры имеют «типы», а передаваемые значения проверяются во время компиляции, как параметры для функций. Допустимые типы - это ваши обычные типы C ++, например int и char. Когда вы создаете экземпляр класса шаблона, вы передаете значение указанного вами типа, и в новой копии определения класса шаблона это значение подставляется везде, где имя параметра было в исходном определении. Прямо как макрос.

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

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

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

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

Джонатан
источник