Статьи

C ++ сжато: шаблоны

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

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


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

Образец: TemplatesSample \ PeekLastItem.h

1
2
3
4
5
6
7
#pragma once
 
template<class T, class U>
U PeekLastItem(T& collection)
{
    return *collection.rbegin();
}

Создание любого шаблона — функции или класса — начинается с ключевого слова template, за которым следуют параметры в квадратных скобках, как показано в предыдущем примере с классом T и классом U. Использование слова class перед T и U не означает аргументы должны быть классами. Вместо этого думайте о классе как об общем слове, предназначенном для передачи значения неспецифического типа. У вас может быть шаблон с конкретными типами или со смесью неспецифических типов классов и конкретных типов, таких как template<class T, int> . Использование T в качестве имени для первого аргумента и U для второго — обычная практика, а не требование. Вы можете использовать почти что угодно в качестве имени аргумента шаблона.

Предыдущая функция принимает ссылку на элемент типа T. Предполагается, что T будет иметь функцию-член с именем rbegin , которая может быть вызвана без аргументов, и будет возвращать тип указателя, который при rbegin ссылки станет объектом типа U. Эта конкретная функция предназначена в первую очередь для работы со многими классами коллекций стандартной библиотеки C ++, хотя любой класс, который соответствует предположениям, которые делает функция в отношении типа T, может использоваться с этой функцией шаблона. Эта способность принимать любой тип, добавлять к нему необходимые функциональные возможности и, таким образом, делать его пригодным для использования с шаблоном, является основным преимуществом шаблонов.


Шаблонные классы похожи на шаблонные функции, только они являются классами, а не простыми автономными функциями. Давайте посмотрим на пример.

Образец: TemplatesSample \ SimpleMath.h

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#pragma once
template<class T>
class SimpleMath
{
public:
 
    SimpleMath(void) { }
 
    ~SimpleMath(void) { }
 
    T Add(T a, T b)
    {
        return a + b;
    }
 
    T Subtract(T a, T b)
    {
        return a — b;
    }
 
    T Multiply(T a, T b)
    {
        return a * b;
    }
 
    T Divide(T a, T b)
    {
        return a / b;
    }
};

Как следует из названия, этот класс не предназначен для демонстрации. Существует только один тип аргумента. Глядя на определение класса, мы можем сделать вывод, что требования для T в этом случае состоят в том, что он имеет следующие операторы, каждый из которых работает с двумя экземплярами T и возвращает экземпляр T:

  • +
  • *
  • /

Хотя они логически связаны с числами, вы можете определить эти операторы для любого класса или типа данных, а затем создать экземпляр этого класса-шаблона, который специализирован для вашего пользовательского класса (например, класса Matrix).

И последнее замечание: если бы вы определяли функции-члены вне определения класса, но все же находились в том же заголовочном файле, конечно, вам нужно было бы использовать ключевое слово inline в объявлениях; определения будут выглядеть так: SimpleMath::SimpleMath(void) { } .

Это последний файл этого примера, показывающий простое использование каждого из предыдущих шаблонов.

Образец: TemplatesSample \ TemplatesSample.cpp

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include <iostream>
#include <ostream>
#include <vector>
#include «SimpleMath.h»
#include «PeekLastItem.h»
#include «../pchar.h»
 
using namespace std;
 
int _pmain(int /*argc*/, _pchar* /*argv*/[])
{
    SimpleMath<float> smf;
 
    wcout << «1.1F + 2.02F = » << smf.Add(1.1F, 2.02F) << «F.»
        endl;
 
    vector<const wchar_t*> strs;
    strs.push_back(L»Hello»);
    strs.push_back(L»World»);
 
    wcout << L»Last word was ‘» <<
        PeekLastItem<std::vector<const wchar_t*>,const wchar_t*>(strs) <<
        L»‘.»
 
    return 0;
}

Функции шаблонов помогают вам повторно использовать код, что, в свою очередь, делает вашу базу кода более легкой в ​​управлении и СУХОЙ (не повторяйте себя). Лямбда-выражения являются предметом следующей статьи этой серии.

Этот урок представляет собой главу из C ++ Succinctly , бесплатной книги от команды Syncfusion .