Использование шаблонов в C++

Использование шаблонов в C++ Программирование и разработка

Использование шаблонов в C++

В базовом программировании на C ++ тип данных, например int или char, должен быть указан в объявлении или определении. Такое значение, как 4, 22 или −5, является целым. Такое значение, как «A», «b» или «c», является символом. Механизм шаблонов позволяет программисту использовать универсальный тип для набора фактических типов. Например, программист может решить использовать идентификатор T для int или char. Алгоритм C ++ может иметь более одного универсального типа. С, скажем, T для int или char, U может обозначать тип с плавающей запятой или указатель. Класс, такой как строковый или векторный класс, подобен типу данных, а созданные экземпляры объектов подобны значениям типа данных, который является указанным классом. Таким образом, механизм шаблонов также позволяет программисту использовать идентификатор универсального типа для набора классов.

Шаблон C ++ создает алгоритм, независимый от типа используемых данных. Таким образом, один и тот же алгоритм с множеством экземпляров одного и того же типа может использовать разные типы при разных исполнениях. Сущности переменной, функции, структуры и класса могут иметь шаблоны. В этой статье объясняется, как объявлять шаблоны, как определять шаблоны и как их применять в C ++. Вы должны уже знать вышеупомянутые сущности, чтобы понимать темы, затронутые в этой статье.

Типы

Скалярный

Скалярные типы: void, bool, char, int, float и pointer.

Классы как типы

Конкретный класс можно рассматривать как тип, а его объекты — как возможные значения.

Универсальный тип представляет собой набор скалярных типов. Список скалярных типов обширен. Тип int, например, имеет другие связанные типы, такие как short int, long int и т. Д. Универсальный тип также может представлять набор классов.

Читайте также:  Добавить в массив в Ruby

Переменная

Пример объявления и определения шаблона следующий:

template<typename T>
T pi = 3.14;

Прежде чем продолжить, обратите внимание, что такого рода операторы не могут появляться в функции main () или в любой области блока. Первая строка — это объявление заголовка шаблона с выбранным программистом универсальным именем типа T. Следующая строка — это определение идентификатора pi, который относится к универсальному типу T. Точность того, является ли T. int, float или какой-либо другой тип может быть выполнен в функции C ++ main () (или какой-либо другой функции). Такая точность будет сделана с переменной pi, а не T.

Первая строка — это объявление заголовка шаблона. Это объявление начинается с зарезервированного слова, шаблона, а затем с открытой и закрытой угловых скобок. В угловых скобках указан как минимум один идентификатор универсального типа, например T, указанный выше. Может быть несколько идентификаторов универсального типа, каждому из которых предшествует зарезервированное слово typename. Такие универсальные типы в этой позиции называются параметрами шаблона.

Следующий оператор может быть записан в main () или в любой другой функции:

cout << pi<float> << \n;

И функция отобразит 3.14. Выражение pi определяет точный тип T для переменной pi. Специализация определяет конкретный тип данных для параметра шаблона. Создание экземпляра — это внутренний процесс C ++ по созданию определенного типа, такого как float, в данном случае. Не путайте создание экземпляра параметра шаблона и создание экземпляра класса. В теме шаблона многие типы данных могут иметь одно общее имя типа, тогда как многие классы могут иметь одно общее имя класса. Однако общее имя класса для классов просто называется классом, а не именем класса. Кроме того, значение относится к типу данных, например int, так же как созданный объект относится к классу, например классу String.

При специализации выбранный тип данных, например float, помещается в угловые скобки после переменной. Если в объявлении заголовка шаблона содержится более одного параметра шаблона, в выражении специализации будет соответствующее количество типов данных в том же порядке.

В специализации тип известен как аргумент шаблона. Не путайте это с аргументом функции для вызова функции.

Тип по умолчанию

Если при специализации тип не указан, предполагается тип по умолчанию. Итак, из следующего выражения:

    template<typename U = const char*>
U pi = «love»;

the display from:

cout << pi<> << \n;

«любовь» к постоянному указателю на char. Обратите внимание в объявлении, что U = const char *. Угловые скобки будут пустыми при специализации (тип не указан); фактический тип считается константным указателем на char, тип по умолчанию. Если бы при специализации требовался какой-то другой тип, то имя типа записывалось бы в угловых скобках. Если для специализации требуется тип по умолчанию, повторение типа в угловых скобках необязательно, т. Е. Угловые скобки можно оставить пустыми.

Примечание: тип по умолчанию все еще можно изменить при специализации, установив другой тип.

Структура

В следующем примере показано, как параметр шаблона можно использовать со структурой:

    template<typename T> struct Ages
{
T John = 11;
T Peter  = 12;
T Mary  = 13;
T Joy   = 14;
};

Это возраст учащихся класса (класса). Первая строка — это объявление шаблона. Тело в фигурных скобках — это фактическое определение шаблона. Возраст можно вывести в функции main () следующим образом:

    Ages<int> grade7;
cout << grade7.John << ‘ ‘ << grade7.Mary << \n;

Результат: 11 13. Первый оператор здесь выполняет специализацию. Обратите внимание, как это было сделано. Он также дает имя для объекта структуры: grade7. Второй оператор имеет обычные выражения объекта структуры. Структура похожа на класс. Здесь Ages похож на имя класса, а grade7 — это объект класса (структура).

Если некоторые значения возраста являются целыми числами, а другие — числами с плавающей запятой, то структуре требуются два общих параметра, как показано ниже:

    template<typename T, typename U> struct Ages
{
T John = 11;
U Peter  = 12.3;
T Mary  = 13;
U Joy   = 14.6;
};

Соответствующий код для функции main () выглядит следующим образом:

    Ages<int, float> grade7;
cout << grade7.John << ‘ ‘ << grade7.Peter << \n;

Результат: 11 12.3. При специализации порядок типов (аргументов) должен соответствовать порядку универсальных типов в объявлении.

Объявление шаблона можно отделить от определения следующим образом:

    template<typename T, typename U> struct Ages
{
T John;
U Peter;
T Mary;
U Joy;
};

Ages<int, float> grade7 = {11, 12.3, 13, 14.6};

Первый сегмент кода — это просто объявление шаблона (нет присваиваний). Второй сегмент кода, который является просто оператором, является определением идентификатора grade7. Слева — объявление идентификатора grade7. Справа находится список инициализаторов, который присваивает соответствующие значения элементам структуры. Второй сегмент (оператор) можно записать в функцию main (), в то время как первый сегмент остается вне функции main ().

Не тип

Примеры типов, не относящихся к данным, включают int, указатель на объект, указатель на функцию и типы auto. Есть и другие нетипы, которые в этой статье не рассматриваются. Нетип подобен неполному типу, значение которого задается позже и не может быть изменено. В качестве параметра он начинается с определенного не типа, за которым следует идентификатор. Значение идентификатора дается позже, при специализации, и не может быть изменено снова (как константа, значение которой задается позже). Следующая программа иллюстрирует это:

#include <iostream>
using namespace std;

template<typename T, typename U, int N> struct Ages
{
T John = N;
U Peter = 12.3;
T Mary = N;
U Joy  = 14.6;
};

int main()
{
Ages<int, float, 11> grade7;

cout << grade7.John << ‘ ‘ << grade7.Joy << \n;

return 0;
}

При специализации первый тип, int, в угловых скобках используется скорее для формальности, чтобы убедиться, что количество и порядок параметров соответствуют количеству и порядку типов (аргументов). Значение N дано при специализации. Результат: 11 14.6.

Частичная специализация

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

#include <iostream>
using namespace std;

//base template class
template<typename T1, typename T2>
struct Ages
{
};

//partial specialization
template<typename T1>
struct Ages<T1, float>
{
T1 John = 11;
float Peter  = 12.3;
T1 Mary  = 13;
float Joy   = 14.6;
};

int main()
{
Ages<int, float> grade7;
cout << grade7.John << ‘ ‘ << grade7.Joy << \n;

return 0;
}

Определите объявление базового класса и определение его частичного класса. Объявление базового класса в заголовке шаблона имеет все необходимые общие параметры. Объявление заголовка шаблона класса частичной специализации имеет только общий тип. В схеме используется дополнительный набор угловых скобок, который идет сразу после имени класса в определении частичной специализации. Это то, что на самом деле делает частичную специализацию. Он имеет тип по умолчанию и тип, отличный от типа по умолчанию, в порядке, указанном в базовом классе. Обратите внимание, что типу по умолчанию по-прежнему можно присвоить другой тип в функции main ().

Соответствующий код в функции main () может быть следующим:

    Ages<int, float> grade7;
cout << grade7.John << ‘ ‘ << grade7.Joy << \n;

Результат: 11 14.6.

Пакет параметров шаблона

Пакет параметров — это параметр шаблона, который принимает ноль или более универсальных типов шаблона для соответствующих типов данных. Параметр пакета параметров начинается с зарезервированного слова typename или class. Далее следуют три точки, а затем идентификатор упаковки. Следующая программа показывает, как пакет параметров шаблона может использоваться со структурой:

#include <iostream>
using namespace std;

template<typename … Types> struct Ages
{
int John = 11;
float Peter  = 12.3;
int Mary  = 13;
float Joy   = 14.6;
};

int main()
{

Ages<int> gradeB;
cout << gradeB.John << ‘ ‘ << gradeB.Mary << \n;

Ages<float> gradeC;
cout << gradeC.Peter << ‘ ‘ << gradeC.Joy << \n;

Ages<int, float> gradeD;
cout << gradeD.John << ‘ ‘ << gradeD.Joy << \n;

Ages<> gradeA;  //like default
cout << gradeA.John << ‘ ‘ << gradeA.Joy << \n;

return 0;
}

Результат:

11 13
12,3 14,6
11 14,6
11 14,6

Шаблоны функций

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

#include <iostream>
using namespace std;

template<typename T, typename U> void func (T no, U cha, const char *str )
{
cout << «There are « << no << » books worth « << cha << str << » in the store.» << \n;
}

int main()
{
func(12, ‘$’, «500»);

return 0;
}

Результат выглядит следующим образом:

В магазине 12 книг стоимостью 500 долларов.

Отделение от прототипа

Определение функции можно отделить от ее прототипа, как показано в следующей программе:

#include <iostream>
using namespace std;

template<typename T, typename U> void func (T no, U cha, const char *str );

template<typename T, typename U> void func (T no, U cha, const char *str )
{
cout << «There are « << no << » books worth « << cha << str << » in the store.» << \n;
}

int main()
{

func(12, ‘$’, «500»);

return 0;
}

Примечание. Объявление шаблона функции не может появляться в функции main () или в любой другой функции.

Перегрузка

Перегрузка одной и той же функции может происходить с разными объявлениями заголовка шаблона. Следующая программа иллюстрирует это:

#include <iostream>
using namespace std;

template<typename T, typename U> void func (T no, U cha, const char *str )
{
cout << «There are « << no << » books worth « << cha << str << » in the store.» << \n;
}

template<typename T> void func (T no, const char *str )
{
cout << «There are « << no << » books worth $» << str << » in the store.» << \n;
}

int main()
{
func(12, ‘$’, «500»);
func(12, «500»);

return 0;
}

Результат:

В магазине 12 книг стоимостью 500 долларов.

В магазине 12 книг стоимостью 500 долларов.

Шаблоны классов

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

#include <iostream>
using namespace std;

class TheCla
{
public:
int num;
static char ch;

void func (char cha, const char *str)
{
cout << «There are « << num << » books worth « << cha << str << » in the store.» << \n;
}
static void fun (char ch)
{
if (ch == ‘a’)
cout << «Official static member function» << \n;
}
};

int main()
{
TheCla obj;
obj.num = 12;
obj.func(‘$’, «500»);

return 0;
}

Результат выглядит следующим образом:

В магазине 12 книг стоимостью 500 долларов.

Следующая программа представляет собой указанную выше программу с объявлением заголовка шаблона:

#include <iostream>
using namespace std;

template<class T, class U> class TheCla
{
public:
T num;
static U ch;

void func (U cha, const char *str)
{
cout << «There are « << num << » books worth « << cha << str << » in the store.» << \n;
}
static void fun (U ch)
{
if (ch == ‘a’)
cout << «Official static member function» << \n;
}
};

int main()
{
TheCla<int, char> obj;
obj.num = 12;
obj.func(‘$’, «500»);

return 0;
}

Вместо слова typename в списке параметров шаблона можно использовать слово class. Обратите внимание на специализацию в объявлении объекта. Результат все тот же:

В магазине 12 книг стоимостью 500 долларов.

Разделительная декларация

Объявление шаблона класса можно отделить от кода класса следующим образом:

template<class T, class U> class TheCla;

template<class T, class U> class TheCla
{
public:
T num;
static U ch;

void func (U cha, const char *str)
{
cout << «There are « << num << » books worth « << cha << str << » in the store.» << \n;
}
static void fun (U ch)
{
if (ch == ‘a’)
cout << «Official static member function» << \n;
}
};

Работа со статическими членами

В следующей программе показано, как получить доступ к статическому члену данных и статической функции-члену:

using namespace std;

template<class T, class U> class TheCla
{
public:
T num;
static U ch;

void func (U cha, const char *str)
{
cout << «There are « << num << » books worth « << cha << str << » in the store.» << \n;
}
static void fun (U cha)
{
if (ch == ‘a’)
cout << «Official static member function» << cha << \n;
}
};

template<class T, class U> U TheCla<T, U>::ch = ‘a’;

int main()
{
TheCla<int, char>::fun(‘.’);

return 0;
}

Присвоение значения статическому элементу данных является объявлением и не может быть в main (). Обратите внимание на использование и расположение универсальных типов и универсального типа данных в операторе присваивания. Кроме того, обратите внимание, что функция-член статических данных была вызвана в main () с фактическими типами данных шаблона. Результат следующий:

Официальная статическая функция-член.

Компиляция

Объявление (заголовок) и определение шаблона должны быть в одном файле. То есть они должны быть в одной единице перевода.

Заключение

Шаблоны C ++ делают алгоритм независимым от типа используемых данных. Сущности переменной, функции, структуры и класса могут иметь шаблоны, которые включают объявление и определение. Создание шаблона также включает в себя специализацию, когда универсальный тип принимает фактический тип. Объявление и определение шаблона должны быть в одной единице перевода.

Оцените статью
bestprogrammer.ru
Добавить комментарий