Що означає шаблон <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
}

11
Ви також можете використовувати тип 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
};

Йоганне, шаблони подаються під "типами"? Я думав, що це такі, з яких типів можна виготовити, але не самі типи?
sbi

@sbi див. пояснення: "Переглянувши це в стандарті, мені довелося перемістити шаблони класів вгору в розділ типів - навіть незважаючи на те, що шаблони не типи. Але вони називаються типом параметрів з метою опису цих видів. ". Зноска 126 від 14.1 / 2 говорить про це. Це просто класифікація, зроблена для того, щоб зробити нетипові параметри чимось, що оголошує значення / посилання та параметри типу, чимось декларуючи ім'я типу або ім'я шаблону.
Йоханнес Шауб - ліб

@ JohannesSchaub-litb, тому немає способу ввести шаблон із скажімо std :: string? як шаблон <std :: string S> класу з деяким статичним лічильником у ньому, щоб створити унікальний ідентифікатор для кожної різної строки? хешшер-рядок до int був би єдиним способом, на жаль, правда?
relaxxx

1
Я хотів би, щоб ця відповідь була доповнена об'єктами члена шаблону класу, тобто шаблоном <typename C, typename R, typename P1, typename P2> structure mystruct <R (C :: *) (P1, P2)>
Johnny Pauling

Частина коду з SillyExampleне може бути скомпільована GCC 4.8.4. Перша помилка - це the value of ‘flag’ is not usable in a constant expression. Є й інші помилки
HEKTO

17

Ви шаблонуєте свій клас на основі "непідписаного 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;
}

Звичайно, версія шаблону в мільярд разів безпечніша та гнучкіша.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.