Частенько во время написания кода возникает необходимость внесения в алгоритм каких-то уточнений для каких-то конкретных случаев или типов. Обычно это делается путем добавления каких-то переменных состояния, перегрузки функций, добавления аргументов в вызовы. Но существует более гибкий способ с т.н. классами свойств и стратегий.
tepmlates again, huhРассмотрим на примере: пусть имеется задача вычисления суммы последовательности значений, хранящихся в массиве и у нас имеются 2 указателя - на первый элемент и на элемент, идущий за последним. Естественно, потребуется написать шаблон, применимый для разных типов:
template <typename T>
inline T accum (T const* beg, T const * end)
{
T total = T (); // для базовых типов равносильно инициализации нулем
while (beg != end)
total += *beg++;
return total;
}
На данном этапе все прекрасно и гламурно и в большинстве случаев даже будет работать. Однако если подсунуть в шаблон указатели на, скажем, char, то уже при небольших размерах массива возможна ошибка переполнения - ведь шаблон возвращает char! Эту проблему можно решить, введя дополнительный параметр шаблона AccT, описывающий тип, который используется переменной total. Но тогда все пользователи должны были бы указывать этот тип при обращении к шаблону, что весьма не кавайно.
Альтернативой к введению параметра является создание связи между типом T и типом, который будет использоваться для хранения результата. Эта связь может рассматриваться в качестве характеристики типа Т и поэтому он иногда называется свойством (trait) T. Эта связь может быть закодирована в виде специализации шаблона:
// объявляем основной шаблон. Реализация не нужна - все будет в специализациях.
template <typename T>
class AccumulationTraits;
template <>
class AccumulationTraits<char>
{
public:
typedef int AccT;
};
template <>
class AccumulationTraits<short>
{
public:
typedef int AccT;
};
template <>
class AccumulationTraits<int>
{
public:
typedef long AccT;
};
template <>
class AccumulationTraits<unsigned int>
{
public:
typedef unsigned long AccT;
};
template <>
class AccumulationTraits<float>
{
public:
typedef double AccT;
};
// и т.д.
Шаблон AccumulationTraits является шаблоном свойств (traits template), поскольку хранит свойство типа своего параметра. Тогда шаблон accum () примет вид:
template <typename T>
inline typename AccumulationTraits <T>::AccT accum (T const* beg, T const* end)
{
typedef typename AccumulationTraits <T>::AccT AccT;
AccT total = AccT (); // для базовых типов равносильно инициализации нулем
while (beg != end)
total += *beg++;
return total;
}
Хотя изменения не особо впечатляют, в шаблон был добавлен механизм настройки алгоритма. Кроме того, если появляются новые типы для accum (), то соответствующий тип AccT может быть связан посредством простого объявления дополнительной явной специализации класса AccumulationTraits.
Вообще кроме информации о типах, имеющей отношение к "главному" типу, с помощью свойств можно передавать еще и константы и прочие классы значений. Для этого достаточно включить в шаблон AccumulationTraits соответствующий метод или значение.Продолжение таки следует...
В материале использовалась книга Д. Вандервурда и Н. М. Джоссатиса "Шаблоны С++. Справочник разработчика".UPD: Наконец-то переоформил вставки с кодом.
@темы:
программистское,
шаблоны
и увидел он только край солнца и оно уже ослепило его
(с) Norritt
Norritt, это все та книжка о шаблонах )
И я сплю нормально, ага )