Отождествление типов аргументов

Так как компилятор генерирует экземпляры шаблонов функций согласно типам, заданным при их вызовах, то критическим моментом является передача корректных типов, особенно если шаблон функции имеет два или более параметров. Хорошим примером является классическая функция max():

template <class T>

T max (T a, T b)

{ return a > b? a: b;

}

Функция max() будет работать правильно, если оба ее аргумента имеют один и тот же тип:

int i = max (1, 2);

double d = max (1.2, 3.4);

Однако, если аргументы различных типов, то вызов max() приведет к ошибке, так как компилятор не сможет понять, что ему делать.

Один из возможных способов для разрешения неоднозначности состоит в использовании приведения типов, чтобы прояснить наши намерения:

int i = max ((int)'a', 100);

Вторая возможность - это явно объявить версию экземпляра шаблона функции перед ее вызовом:

int max (int, int);

int j = max ('a', 100);

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

template <class T1, class T2>

T1 max (T1 a, T2 b)

{ return a > (T1)b? a: (T1)b;

}

Использование этой новой версии max() не приведет к неоднозначности в случае использования двух различных типов. Например, если написать

max ('a', 100);

то компилятор будет использовать два заданных (посредством аргументов типа) и построит версию функции max() с заголовком

char max (char, int);

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

max ('a', 100)

дает значение типа char, в то время как

max (100, 'a')

передает в вызывающую программу int.

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

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

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

template <class T>

class Pair

{ T a, b;

public:

Pair (T t1, T t2);

T Max();

T Min ();

int isEqual ();

};

Пока все выглядит также изящно, как и для шаблонов функций. Единственная разница состоит в том, что вместо описания функции используется объявление класса. Шаблоны классов становятся все более сложными, когда вы описываете принадлежащие функции класса. Вот, например, описание принадлежащей функции Min() класса Pair:

template <class T>

T Pair <T>::Min()

{ return a < b? a: b;

}

Чтобы понять эту запись, давайте вернемся немного назад. Если бы Pair был обычным классом (а не шаблоном класса) и T был бы некоторым конкретным типом, то функция Min класса Pair была бы описана таким образом:

T Pair::Min()

{ return a < b? a: b;

}

Для случая шаблонной версии нам необходимо, во-первых, добавить заголовок шаблона template <class T>

Затем нужно дать имя классу. Помните, что на самом деле мы описываем множество классов - семейство Pair. Повторяя синтаксис префикса (заголовка) шаблона, экземпляр класса Pair для целых типов, можно назвать Pair<int>, экземпляр для типа double - Pair<double>, для типа Vector - Pair<Vector>. Однако в описании принадлежащей классу функции нам необходимо использовать имя класса Pair<T>. Это имеет смысл, так как заголовок шаблона говорит, что Т означает имя любого типа.

Приведем текст остальных методов класса Pair. Описания методов помещаются в заголовочный файл, так как они должна быть видимы везде, где используется класс Pair.

// конструктор

template <class T>

Pair <T>::Pair (T t1, T t2): a(t1), b(t2)

{}

// метод Max

template <class T>

T Pair <T>::Max()

{ return a>b? a: b;

}

// метод isEqual

template <class T>

int Pair <T>::isEqual()

{ if (a==b) return 1;

return 0;

}

Ранее уже отмечалось, что шаблоны функций могут работать только для тех (встроенных) типов данных или классов, которые поддерживают необходимые операции. То же самое справедливо и для шаблонов классов. Чтобы создать экземпляр класса Pair для некоторого классового типа, например для класса X, этот класс должен содержать следующие общедоступные функции

X (X &);    // конструктор копирования

int operator == (X)

int operator < (X);

Три указанные функции необходимы, так как они реализуют операции, выполняемые над объектами типа T в метода X шаблона класса Pair.

Если вы собираетесь использовать некоторый шаблон класса, как узнать какие операции требуются? Если шаблон класса снабжен документацией, то эти требования должны быть в ней указаны. В противном случае придется читать первичную документацию - исходный текст шаблона. При этом учитывайте, что встроенные типы данных поддерживают все стандартные операции.


Понравилась статья? Добавь ее в закладку (CTRL+D) и не забудь поделиться с друзьями:  



double arrow
Сейчас читают про: