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

Альтернатива:

Добавление пользовательских специализаций сопряжено с рядом трудностей: вероятно, придется закрыть пространства имен, в которых ведется работа и открыть пространство имен шаблона свойств iterator_traits, записать текст специализации свойств непосредственно –это не очень эффективно, по сравнению с вложенным определением типа - единственная информация, которая действительно значит что-либо.

Стандартная библиотека обеспечивает способ, который позволяет автору итератора управлять типами, вложенными в его iterator_traits только при записи типов элемента в iterator. Основной шаблон iterator_traits отыскивает iterator, чтобы захватить его типы элемента:

Стандарт C++ ссылается на обычные объявления шаблона и определения настроенный против частичных или явных (полных) специализаций основных (первичных) шаблонов.

template <class Iterator>

struct iterator_traits

{

typedef typename Iterator::value_type value_type;

//...four more typedefs

};

Это «дополнительный уровень абстракции» в работе: вместо того, чтобы идти обращаться непосредственно к Iterator::value_type, алгоритм iter_swap обращается к iterator_traits - value_type. Если некоторая специализация не переопределяет основной шаблон iterator_traits, iter_swap видит тот же самый value_type, который бы использовался во вложенном типе iterator.

Метафункции Продвижение (пересылка) метафункций

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

Структуры по умолчанию открыты, и их наследование тоже, причина: совместимость с языком С в котором нет инкапсуляции.

struct a{enum {_a = 1};};

struct b : a{}; // по умолчанию открытое наследование

int main()

{

cout << b::_a << endl; // применяем продвижение

return 0;

}

Класс метафункции

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

Метафункция - центральная абстракция MPL (фундаментальная единица). Это шаблонный класс, все параметры являются типами. В открытом доступе находится вложенный элемент «::type/::value», в отличии от свойств может выполнять действия.

Формат:

Имя_Метафункции<Типы параметров>

{

метафункция::type

метафункция::value

//…

};

Вызов метафункции и получение ее результата:

Имя_метафункции<аргументы>::type/::value;

Первая часть записи – вызов метафункции:

Имя_метафункции<аргументы>…

Вторая часть записи – получение результата работы метафункции:

…::type/::value;

Различие между метафункциями и классическими свойствами:

Шаблоны свойств в стандартной библиотеке все следуют модели «множественных результатов». Мы именуем этот вид шаблона свойств как «blob» ((Binary Large Object) большой двоичный объект), такие свойства подобны связке метафункций, объединенных в единый блок - эта идиома создает больше проблем, чем решений, поэтому лучше ее избегать.

Проблема эффективности:

  • Обращении к iterator_traits::value_type требует от компилятора много дополнительной (не нужный в данный момент) работы: помимо value_type, необходимо вычислить и четыре других связанных типа.

  • «блоб» вмешивается в нашу возможность записать метафункции, которые принимают другие метафункции в качестве параметров.

Рассмотрим тривиальную функцию времени выполнения, которая принимает два аргумента:

template <class X, class UnaryOp1, class UnaryOp2>

X apply_fg(X x, UnaryOp1 f, UnaryOp2 g)

{

return f(g(x));

}

Объединим f и g в единственный «блоб» параметр, т.е. в теле объекта Блоб одновременно существует 2 функции: f и g:

template <class X, class Blob>

X apply_fg(X x, Blob blob)

{

return blob.f(blob.g(x));

}

Такой вызов f и g аналогичен доступу к «блобу свойств»: чтобы получить результат «функции» необходимо обратится к одному из элементов объекта. Проблема в том, что нет единственного способа получить результат вызова одного из этих блобов или изменить извне последовательность вызовов этих функий в теле apply_fg. Каждая функция наподобии apply_fg будет использовать свой собственный набор имен функций-класса, и чтобы передать f или g другой такой функции. Решение: привнести в эту модель модульность.

«Блоб» является антипаттерном (идиома, которую избегают), потому что он уменьшает полную функциональную совместимость программы, или возможность ее компонентов работать гибко и гладко вместе.

Хорошим решением будет apply_fg, которая принимает каждую функцию в виду отдельного аргумента – это поддерживает модульность и функциональную совместимость.

Когда вызываемые параметры apply_fg используют единственный протокол, мы можем легко обмениваться ими:

#include <functional>

float log2(float);

int a = apply_fg(5.0f, std::negate<float>(), log2);

int b = apply_fg(3.14f, log2, std::negate<float>());

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

Такой подход порождает полиморфизм: Свойство, которое позволяет различным типам параметра использоваться взаимозаменяемо; буквально, «возможность принимать многообразие форм.»

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