Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
template_metaprogramming_ru.docx
Скачиваний:
12
Добавлен:
01.07.2025
Размер:
774 Кб
Скачать
☆

Пример:

//+--------------------------------------------------------------------+

// Задание 1:

template<class T> struct add_const_ref

{

typedef const T & type;

};

//Задание 2:

template<class T1, class T2, class T3> struct replace_type

{

typedef T3 type;

};

template<class T1, class T2> struct replace_type<T1*, T1, T2>

{

typedef T2* type;

};

template<class T1, class T2, size_t N> struct replace_type<T1 const*[N], const T1, T2>

{

typedef const T2*(*type)[N];

};

template<class T1, class T2> struct replace_type<T1(*)(T1), T1, T2>

{

typedef T2(*type)(T2) ;

};

int main()

{

// Задание 1:

cout << typeid(add_const_ref<int>::type).name() << endl;

// Задание 2:

typedef replace_type< void*, void, int >::type t1; // int*

cout << typeid(t1).name() << endl;

typedef replace_type<

int const*[10]

, int const

, long

>::type t2; // long* [10]

cout << typeid(t2).name() << endl;

typedef replace_type<

char& (*)(char&)

, char&

, long&

>::type t3; // long& (*)(long&)

cout << typeid(t3).name() << endl;

// Задание 3:

return 0;

}

Типовые ассоциации

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

Рассмотрим алгоритм работы с типами iter_swap:

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

template <class ForwardIterator1, class ForwardIterator2>

void iter_swap(ForwardIterator1 i1, ForwardIterator2 i2)

{

T tmp = *i1;

*i1 = *i2;

*i2 = tmp;

}

«T» - тип, но в такой записи он не будет распознан и это приведет к ошибке компиляции. Необходимо объяснить компилятору, что работа ведется с типом.

Решение:

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

template <class ForwardIterator1, class ForwardIterator2>

void iter_swap(ForwardIterator1 i1, ForwardIterator2 i2)

{

typename ForwardIterator1::value_type tmp = *i1;

*i1 = *i2;

*i2 = tmp;

}

*Стандарт C++ требует ключевого слова typename, когда используется зависимое имя (с помощью «::») для обращения к типу.

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

void f(int* p1, int* p2)

{

iter_swap(p1,p2); // error: int* has no member 'value_type'

}

Решение: введение дополнительного уровня абстракции – теорема FTSE.

Согласно этой теореме, мы, возможно, не в состоянии добавить вложенный «:: value_type» ко всем итераторам, но мы можем добавить это к шаблону, который принимает тип итератора в качестве параметра – это и будет дополнительная абстракция, решающая проблему.

В стандартной библиотеке этот шаблон, назвывается iterator_traits:

template <class Iterator> struct iterator_traits;

Применим эту идею к алгоритму iter_swap:

template <class ForwardIterator1, class ForwardIterator2>

void iter_swap(ForwardIterator1 i1, ForwardIterator2 i2)

{

typename

iterator_traits<ForwardIterator1>::value_type tmp = *i1;

*i1 = *i2;

*i2 = tmp;

}

Шаблон iterator_traits описвает пять свойств для типа:

  • value_type;

  • reference;

  • pointer;

  • difference_type;

  • iterator_category;

Наибольшая польза iterator_traits: они дают нам способ связать информацию с типом.

Например: ваш злобный коллега, Гектор дает вам некоторый тип подобный iterator, названный hands_off, который обращается к интервалу, можно обратится к его value_type, не нарушая гармонии рабочей группы. Все, что необходимо сделать -добавить частичную специализация iterator_traits, и iter_swap будет видеть интервал типа, посредством value_type для итератора Гектора:

namespace std

{

template <>

struct iterator_traits<Hector::hands_off>

{

typedef int value_type;

//four more typedefs...

};

}

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

template <class T>

struct iterator_traits<T*>

{

typedef T value_type;

//...four more typedefs

};

Благодаря косвенности через iterator_traits обобщенные функции могут получить доступ ассоциированным типам однородно, независимот от того является ли тип указателем.

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]