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



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

1 2 3 4 5 6 7 template <class T>   T max (T a, T b)   { return a > b ? a : b;   }

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

1 2 3 int i = max (1, 2);   double d = max (1.2, 3.4);

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

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

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

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

i

1 2 3 nt max (int, int);   int j = max ('a', 100);

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

1 2 3 4 5 6 7 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.

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

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

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

1 2 3 4 5 6 7 8 9 template <class T>   class Pair   { T a, b; public:   Pair (T t1, T t2); T Max(); T Min (); int isEqual ();   };

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

1 2 3 4 5 6 7 template <class T>   T Pair <T>::Min()   { return a < b ? a : b;   }

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

1 2 3 4 5 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.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 // конструктор   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, этот класс дол­жен содержать следующие общедоступные функции

1 2 3 4 5 X (X &); // конструктор копирования   int operator == (X)   int operator < (X);

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

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


Дата добавления: 2021-01-21; просмотров: 104; Мы поможем в написании вашей работы!

Поделиться с друзьями:






Мы поможем в написании ваших работ!