- •Вычисления значений
- •Пример:
- •Вычисления типов
- •Преимущества мета подхода:
- •Польза использования mpl:
- •Числовая метафункция
- •Свойства
- •Метаданные
- •Специализация
- •Первичный шаблон:
- •Инстанцирование:
- •Метафункции
- •Полиморфизм
- •Возможности свойств:
- •Организация свойств в Boost:
- •Пример:
- •Типовые ассоциации
- •Альтернатива:
- •Метафункции Продвижение (пересылка) метафункций
- •Класс метафункции
- •Формат:
- •Числовые
- •Пример:
- •Нульарные
- •Метрический анализ (Анализ размерностей)
- •Метрики
- •Величины
- •Сложение и вычитание
- •Умножение
- •Класс метафункции
- •Деление
- •Пример:
- •Метафункции высшего порядка (Каскады вложенных вызовов)
- •Использование apply:
- •Заполнители
- •Безымянный заполнитель:
- •Определение выражений с заполнителями
- •Лямбда (Лямбда метафункции/выражения)
- •Частичное приложение метафункции
- •Лямбда и Шаблоны Неметафункции
- •Обертки
- •Выбор типа
- •Ленивый Выбор Типа
- •Логические Операторы
- •Целочисленные обертки
- •Составные Операторы
- •Операторы с булевым значением:
- •Операторы для сравнения значений:
- •Операторы для интегральных значений:
- •Побитовые операторы:
- •Арифметические операторы:
- •Целочисленная «стенография»
- •Интегральные последовательности
- •Формат:
- •Последовательности
- •Концепции
- •Итераторы
- •Двунаправленные
- •Произвольные
- •Последовательности
- •Двунаправленные
- •Произвольные
- •Расширенные
- •Ассоциативные
- •Требования
- •Расширения
- •Расширенные ассоциативные последовательности
- •Алгоритмы и последовательности
- •Итераторы
- •Равенство
- •Операции Внутренние операции с последовательностей
- •Наследование
- •Создание последовательности
- •Реализация:
- •Создание итератора
- •Преимущества полной специализации перед частичной:
- •Добавление расширения
- •Алгоритмы
- •Абстракция:
- •Stl алгоритмы
- •Детали правильного использования stl:
- •Mpl алгоритмы
- •Различия stl и mpl алгоритмов:
- •Пример:
- •Вставки
- •Пример 1:
- •Пример 2:
- •Пример 3:
- •Фундамент алгоритмов
- •Пример:
- •Запрашиваемые алгоритмы
- •Последовательности (Алгоритмы, создающие последовательности)
- •Синонимы функциональных алгоритмов:
- •Создание алгоритма
- •Итераторы «Просмотр» и итераторы-адаптеры
- •Концепция просмотра
- •Применение просмотра Сравнения значений элементов последовательности:
- •Объединение множественных (мульти-) последовательностей
- •Упрощение 1
- •Упрощение 2 (создание просмотра)
- •Уход от ненужного вычисления
- •Выборочная обработка элемента
- •Итератор-адаптер
- •Создание просмотра
- •Реализация итератора-адаптера для zip_view.
- •Диагностика
- •Отладка ошибок
- •Трассировка инстанцирования
- •Исправленный пример:
- •Форматы сообщений об ошибках
- •Блок «with»
- •Устранение параметров шаблона по умолчанию
- •Глубокий typedef
- •Инструменты
- •Несколько компиляторов
- •(Статические Утверждения)
- •Формат:
- •Формат:
- •Индивидуальные сообщения утверждений
- •Настройка предиката:
- •(Генерирование встроенных сообщений)
- •Формат:
- •Выбор стратегии:
- •Тип печати
- •Кросс этапность компиляции/выполнения
- •Печать типов:
- •Тип посещения:
- •Выбор реализаций
- •Специализация шаблонов классов
- •Тег диспетчеризации
- •Выбор структуры
- •Композиция классов
- •Указатели на функцию
- •Ослабление типизации
- •Пример:
- •Обобщение:
- •Динамический полиморфизм
- •Автоматизация
- •Сохранение интерфейса
- •Рекуррентный шаблон
- •Формат:
- •Генерирование функций
- •Управление разрешением перегрузки
- •Явное управление множеством перегрузки
- •Документация mpl Последовательности
- •Концепции
- •(Прямая последовательность)
- •(Двунаправленная Последовательность)
- •(Последовательность произвольного доступа)
- •(Расширенная последовательность)
- •(Расширенная последовательность начала)
- •(Расширенная последовательность конца)
- •(Ассоциативная последовательность)
- •(Расширенная ассоциативная последовательность)
- •(Обертка интегральной последовательности )
- •(Переменная последовательность)
- •Представления
- •Метафункции (Внутренние)
- •Итераторы
- •Концепции
- •(Прямой итератор)
- •(Двунаправленный итератор)
- •(Итератор произвольного доступа)
- •Метафункции
- •Алгоритмы
- •Концепции
- •(Инсертер: механизм вставок)
- •Инсертеры (инсертер: механизм вставок)
- •Итеративные алгоритмы
- •Запрашиваемые алгоритмы
- •Преобразования
- •Метафункции
- •Концепции Метафункция
- •Метафункциональный класс
- •Лямбда выражения
- •Заполнители
- •Тэг диспетчеризированные метафункции
- •Числовые метафункции
- •Тривиальные метафункции
Тип посещения:
Для более общего решение проблемы сглаживания типов для кросс-функции, можно применить паттерн Visitor:
struct visit_type // generalized visitation function object
{
template <class Visitor>
void operator()(Visitor) const
{
Visitor::visit();
}
};
template <class T> // specific visitor for type printing
struct print_visitor
{
static void visit()
{
std::cout << typeid(T).name() << std::endl;
}
};
int main()
{
mpl::for_each<s, print_visitor<_1>>(visit_type());
}
Здесь функтор visit_type ожидает, что ее аргумент будет иметь статическую функцию-класса visit. Так, можно строить новые объекты-посетители для любых целей.
Выбор реализаций
Рассмотрим способы выбора поведения или интерфейсов на этапе выполнения, основываясь на некторых вычислениях этапа компиляции.
if
Самый простой способ контролировать реализацию шаблонной функции времени выполнения – проверка статических состояний в заголове инструкции if:
template <class T>
void f(T x)
{
if (boost::is_class<T>::value)
{
//...implementation 1...
}
else
{
//...implementation 2...
}
}
Так как условие может быть полностью определено во время компиляции, то многие компиляторы будут оптимизировать проверку is_class, генерируя код только для выбранной ветки (! Корректно только в том случае, если шаблонные данные обоих ветвей согласованы для типа любого передаваемого аргумента).
Данный метод не является универсальным.
Если логика реализации шаблонных данных в двух ветвях будет отличаться, то передача определенных типов может приводить к сбою.
Например, рассмотрим, что происходит, когда функции выше, применяется следующим образом:
template <class T>
void f(T x)
{
if (boost::is_class<T>::value)
{
std::cout << x::value; // handle integral wrappers
}
else
{
std::cout << x; // handle non-wrappers
}
}
Цель была иметь возможность выводить значение интегрального типа (например, int) или интегральной константной оболочку (например, long_ <5>). Если вызвать f(42), то будет ошибка компиляции. Причина: в теле функции f будут проверены сразу обе ветви инструкции if и передача интегрального типа int приведет к ошибке, т.к. для нее не существует элемента «::value».
Специализация шаблонов классов
Разделим ветви инструкии if на различные статичесные функции-класса нескольких специализаций шаблона. По специализации можно будет решить, какое поведение выбрать:
template <bool> // handle integral constant wrappers
struct f_impl
{
template <class T>
static void print(T x) { std::cout << x::value; }
};
template <> // specialization for non-wrappers
struct f_impl<false>
{
template <class T>
static void print(T x) { std::cout << x; }
};
template <class T>
void f(T x)
{
f_impl<boost::is_class<T>::value>::print(x);
};
Тег диспетчеризации
Теги диспетчеризации времени выполнения использует перегрузку функции для генерирования исполняемого кода на основе свойств типа.
Хорошим примером явялется реализация алгоритма advance стандартной библиотеки. Концептуально данный алгоритм перемещает итератор i на n позиций, в зависимости от вида итератора, выбирается та или иная реализация данного алгоритма, поддерживающая особую логику реализации обхода последовательности итератором конкретного вида. Например, если i поддерживает произвольный доступ, то алгоритм смещения может быть реализована как «i+=n» и является очень эффективным: постоянная сложность независимо от велечины смещения. Для двунаправленного итератора необходимо делать выбор мжду реализацией инкрементации/декрементации, попытка вызвать операцию декрементации для прямого итератора должна приводить к ошибке компиляции.
Таким образом, алгоритм advance требует, по крайней мере три различных реализаций. Выбор одной из них будет зависеть от концепции того или иного типа категории тега:
Иерархия одиночного наследования в виде спсика:
namespace std
{
struct input_iterator_tag { };
struct forward_iterator_tag
: input_iterator_tag { };
struct bidirectional_iterator_tag
: forward_iterator_tag { };
struct random_access_iterator_tag
: bidirectional_iterator_tag { };
}
Тег является просто пустым классом, единственной целью которого - передача некоторой информации во время компиляции, в данном случае моделирование концепции итератора согласно полученному типу итератора. Каждый итератор типа I имеет ассоциированный с ним тег категории, которые могут быть доступны как
std::iterator_traits<I>::iterator_category
В данном случае теги принадлежат иерархии наследования, которая уточняет иерархию концепций, которую они представляют. Например, каждый двунаправленный итератор также явялется прямым итератором, также и тэг двунаправленного итератора наследует тэг прямого итератора.
Выбор реализаций времени выполнения будет осуществляться посредством перегрузки функции и использования тэгов в качестве аргументов.
namespace std
{
//+--------------------------------------------------------------------+
// Реализация итератора ввода.
template <class InputIterator, class Distance>
void __advance_impl
(
InputIterator& i,
Distance n, i
input_iterator_tag
)
{
while (n--) ++i;
}
//+--------------------------------------------------------------------+
// Реализация двунаправленного итератора.
template <class BidirectionalIterator, class Distance>
void __advance_impl
(
BidirectionalIterator& i,
Distance n,
bidirectional_iterator_tag
)
{
if (n >= 0)
while (n--) ++i;
else
while (n++) --i;
}
//+--------------------------------------------------------------------+
// Реализация итератора произвольного доступа.
template <class RandomAccessIterator, class Distance>
void __advance_impl
(
RandomAccessIterator& i,
Distance n,
random_access_iterator_tag
)
{
i += n;
}
//+--------------------------------------------------------------------+
// Алгоритм является диспетчеризатором по тегу.
template <class InputIterator, class Distance>
void advance(InputIterator& i, Distance n)
{
typedef typename
iterator_traits<InputIterator>::iterator_category category;
__advance_impl(i, n, category());
}
}
Польза иерархий наследования:
Выбирается та перегруженная версия __advance_impl, тип параметов которой наиболее соответствует типу тега. Польза иерархии наследования итераторов для работы тегов в том, что нет необходимости создавать реализации __advance_impl, для итераторов производных классов (forward_iterator_tag – производный от input_iterator_tag), так, например, компилятор автоматически выбирает версию input_iterator_tag для всех входных и прямых итераторов. Это было бы невозможным при использовании специализации типов тегов для выбра реализации.
Нюанс: mpl::true_ и mpl::false_ являются тегами диспетчеризации. В примере, приведенном ниже, desperate_cast <T> (х) эквивалентна static_cast <T> (х), если х не является (указатель на) объектом полиморфного класса, в противном случае desperate_cast <T> (х) эквивалентна dynamic_cast <T> (х).
//+--------------------------------------------------------------------+
//| Реализация оболочек для операторов приведения. |
//| Если х не является (указатель на) объектом полиморфного класса, то |
//| desperate_cast<T>(х) == static_cast<T>(х), иначе |
//| desperate_cast<T>(х) == dynamic_cast<T>(х) |
//+--------------------------------------------------------------------+
// implementation for polymorphic types
template <class T, class U>
T desperate_cast_impl2(U& x, mpl::true_)
{
return dynamic_cast<T>(x); // legal iff U is polymorphic
}
//+--------------------------------------------------------------------+
// implementation for non-polymorphic types
template <class T, class U>
T desperate_cast_impl2(U& x, mpl::false_)
{
return static_cast<T>(x);
}
//+--------------------------------------------------------------------+
// dispatcher
template <class T, class U>
T desperate_cast_impl(U& x)
{
return desperate_cast_impl2<T>
(
x,
boost::is_polymorphic<
typename boost::remove_pointer<U>::type
>()
);
}
//+--------------------------------------------------------------------+
// public interface
template <class T, class U>
T desperate_cast(U const& x) { return desperate_cast_impl<T>(x); }
template <class T, class U>
T desperate_cast(U& x) { return desperate_cast_impl<T>(x); }
С помощью механизмов интегральных свойств типа, метафункция is_polymorphic <...>() определит по типу объекта, какой тег необходимо выбрать.
Генератор объекта - обобщенная функция, которая используется для вывода информации о типах, которая иначе может быть выведена только более трудным способом.
Рассмотрим следующий шаблон, содержащий композицию двух вызываемых объектов, f и g. Это функтор-композитор, обеспечивает вложенные вызовы f(g(х)) с аргументом х и возвращает значение типа R:
template <class R, class F, class G>
class compose_fg
{
public:
compose_fg(F const& f, G const& g)
: f(f), g(g)
{}
template <class T>
R operator()(T const& x) const
{
return f(g(x));
}
private:
F f;
G g;
};
Следующий пример использует композитора, примененяя sin2(x) для каждого элемента последовательности:
#include <functional>
#include <algorithm>
#include <cmath>
float input[5] = {0.0, 0.1, 0.2, 0.3, 0.4};
float output[5];
float sin_squared(double x) { return std::sin(std::sin(x)); }
float* ignored =
std::transform(
input, input+5, output,
compose_fg<float, std::negate<float>, float(*)(float)> // явные типы параметров
(
std::negate<float>(), &sin_squared
)
);
Чтобы упростить запись, для автоматического вывода шаблонных параметров класса необходимо использовать специальную функцию посредника – генератор объекта, т.к. функции, в отличии от классов, поддерживают автоматический вывод шаблонных типов:
template <class R, class F, class G>
compose_fg<R,F,G> compose(F const& f, G const& g)
{
return compose_fg<R,F,G>(f,g);
}
Теперь алгоритм std::transform может быть вызван следующим образом:
float* ignored = std::transform(
input, input+5, seq2,
compose<float>(std::negate<float>(), &sin_squared)
);
Так, в списке параметра шаблона необходимо указать только <float>, для определения типа возвращаемого значения.
Стандратные шаблонные функции bind1st и bind2nd аналогичные генераторы объектов, с механизмом связывания.
Для связывания аргументов с параметрами шаблонов по умолчанию, можно использовать библиотеку Boost Bind library, которая предоставляется больше возможностей.
