- •Вычисления значений
- •Пример:
- •Вычисления типов
- •Преимущества мета подхода:
- •Польза использования mpl:
- •Числовая метафункция
- •Свойства
- •Метаданные
- •Специализация
- •Первичный шаблон:
- •Инстанцирование:
- •Метафункции
- •Полиморфизм
- •Возможности свойств:
- •Организация свойств в Boost:
- •Пример:
- •Типовые ассоциации
- •Альтернатива:
- •Метафункции Продвижение (пересылка) метафункций
- •Класс метафункции
- •Формат:
- •Числовые
- •Пример:
- •Нульарные
- •Метрический анализ (Анализ размерностей)
- •Метрики
- •Величины
- •Сложение и вычитание
- •Умножение
- •Класс метафункции
- •Деление
- •Пример:
- •Метафункции высшего порядка (Каскады вложенных вызовов)
- •Использование apply:
- •Заполнители
- •Безымянный заполнитель:
- •Определение выражений с заполнителями
- •Лямбда (Лямбда метафункции/выражения)
- •Частичное приложение метафункции
- •Лямбда и Шаблоны Неметафункции
- •Обертки
- •Выбор типа
- •Ленивый Выбор Типа
- •Логические Операторы
- •Целочисленные обертки
- •Составные Операторы
- •Операторы с булевым значением:
- •Операторы для сравнения значений:
- •Операторы для интегральных значений:
- •Побитовые операторы:
- •Арифметические операторы:
- •Целочисленная «стенография»
- •Интегральные последовательности
- •Формат:
- •Последовательности
- •Концепции
- •Итераторы
- •Двунаправленные
- •Произвольные
- •Последовательности
- •Двунаправленные
- •Произвольные
- •Расширенные
- •Ассоциативные
- •Требования
- •Расширения
- •Расширенные ассоциативные последовательности
- •Алгоритмы и последовательности
- •Итераторы
- •Равенство
- •Операции Внутренние операции с последовательностей
- •Наследование
- •Создание последовательности
- •Реализация:
- •Создание итератора
- •Преимущества полной специализации перед частичной:
- •Добавление расширения
- •Алгоритмы
- •Абстракция:
- •Stl алгоритмы
- •Детали правильного использования stl:
- •Mpl алгоритмы
- •Различия stl и mpl алгоритмов:
- •Пример:
- •Вставки
- •Пример 1:
- •Пример 2:
- •Пример 3:
- •Фундамент алгоритмов
- •Пример:
- •Запрашиваемые алгоритмы
- •Последовательности (Алгоритмы, создающие последовательности)
- •Синонимы функциональных алгоритмов:
- •Создание алгоритма
- •Итераторы «Просмотр» и итераторы-адаптеры
- •Концепция просмотра
- •Применение просмотра Сравнения значений элементов последовательности:
- •Объединение множественных (мульти-) последовательностей
- •Упрощение 1
- •Упрощение 2 (создание просмотра)
- •Уход от ненужного вычисления
- •Выборочная обработка элемента
- •Итератор-адаптер
- •Создание просмотра
- •Реализация итератора-адаптера для zip_view.
- •Диагностика
- •Отладка ошибок
- •Трассировка инстанцирования
- •Исправленный пример:
- •Форматы сообщений об ошибках
- •Блок «with»
- •Устранение параметров шаблона по умолчанию
- •Глубокий typedef
- •Инструменты
- •Несколько компиляторов
- •(Статические Утверждения)
- •Формат:
- •Формат:
- •Индивидуальные сообщения утверждений
- •Настройка предиката:
- •(Генерирование встроенных сообщений)
- •Формат:
- •Выбор стратегии:
- •Тип печати
- •Кросс этапность компиляции/выполнения
- •Печать типов:
- •Тип посещения:
- •Выбор реализаций
- •Специализация шаблонов классов
- •Тег диспетчеризации
- •Выбор структуры
- •Композиция классов
- •Указатели на функцию
- •Ослабление типизации
- •Пример:
- •Обобщение:
- •Динамический полиморфизм
- •Автоматизация
- •Сохранение интерфейса
- •Рекуррентный шаблон
- •Формат:
- •Генерирование функций
- •Управление разрешением перегрузки
- •Явное управление множеством перегрузки
- •Документация mpl Последовательности
- •Концепции
- •(Прямая последовательность)
- •(Двунаправленная Последовательность)
- •(Последовательность произвольного доступа)
- •(Расширенная последовательность)
- •(Расширенная последовательность начала)
- •(Расширенная последовательность конца)
- •(Ассоциативная последовательность)
- •(Расширенная ассоциативная последовательность)
- •(Обертка интегральной последовательности )
- •(Переменная последовательность)
- •Представления
- •Метафункции (Внутренние)
- •Итераторы
- •Концепции
- •(Прямой итератор)
- •(Двунаправленный итератор)
- •(Итератор произвольного доступа)
- •Метафункции
- •Алгоритмы
- •Концепции
- •(Инсертер: механизм вставок)
- •Инсертеры (инсертер: механизм вставок)
- •Итеративные алгоритмы
- •Запрашиваемые алгоритмы
- •Преобразования
- •Метафункции
- •Концепции Метафункция
- •Метафункциональный класс
- •Лямбда выражения
- •Заполнители
- •Тэг диспетчеризированные метафункции
- •Числовые метафункции
- •Тривиальные метафункции
Управление разрешением перегрузки
В своей простейшей форме, CRTP используется для установления отношения наследования между классами, в противном случае, с целью разрешения перегрузки для несвязанных между собой классов, а также, чтобы избегать слишком общих аргументов шаблонных функций.
Например, если мы напишем общобщенную функцию drive, которая оперирует транспортными средствами (где автомобиль это концепция), то можно написать:
template <class Vehicle>
void drive(Vehicle const& v)
{ ... }
Проблема возникает, когда будет написана одноименная функция drive, но с другим типом параметра (например, Screw):
template <class Screw>
void drive(Screw const& s)
{ ... }
Отличие имени шаблонного параметра не является личием типов для компилятора. Если такие фукнции находятся в одном пространстве имен – ошибка компиляции, если в разных - неоднозначность или наршуение правила одного определения, а в случае поиска функции посредством ADL возможен вообще вызов другой функции.
В результате ADL сформирует множество перегруженных функций из которых необходимо будет сделать выбор или выдать ошибку.
#include <list>
namespace utility
{
// fill the range with zeroes
template <class Iterator>
Iterator clear(Iterator const& start, Iterator const& finish);
// perform some transformation on the sequence
template <class Iterator>
int munge(Iterator start, Iterator finish)
{
// ...
start = clear(start, finish);
// ...
}
}
namespace paint
{
template <class Canvas, class Color> // generalized template
void clear(Canvas&, Color const&);
struct some_canvas { };
struct black { };
std::list<some_canvas> canvases(10);
int x = utility::munge(canvases.begin(), canvases.end()); // вызов
}
В данном случае будет ошибка компиляции, т.к. итераторы list являеются шаблонным классом параметризованным как paint::some_canvas. ADL выполняет поиск функции clear в пространстве имен paint, которая добавлена к множеству перегруженных функций. В теле munge вызов print::clear выглядит предпочтительнее, чем utility::clear для передачи аргументов. Т.к. в данном случае тип возаращаемого значения – void, то будет выдана ошибка компиляции, но если бы функция print::clear возвращала Canvas&, то ошибка может быть незамечена, что приведет к неопределенным последствиям.
Решение: применим паттерн CRTP для идентификации модели концепции Vehicle и Screw. Необходимо добавить требование, что бы модели каждой концепции открыто наследовали базовый класс CRTP:
template <class Derived>
struct vehicle
{};
template <class Derived>
struct screw
{};
Теперь шаблонная функция drive может быть переписана, с помощью понижающего приведения:
template <class Vehicle>
void drive(vehicle<Vehicle> const& v)
{
Vehicle const& v_= static_cast<Vehicle const&>(v);
//...
};
template <class Screw>
void drive(screw<Screw> const& s)
{
Screw const& s_= static_cast<Screw const&>(s);
//...
};
Явное управление множеством перегрузки
Иногда, CRTP недостаточно для ограничения достижения обобщенных аргументов шаблонной функции. Например, необходимо, чтобы шаблонная функция оперировала встроенными типами (которые не могут иметь базовые классы), или сторонними типами, которые не должны изменяться. Если мы можем определить типы аргументов во время компиляции, то набор enable_if из Boost, позволяют управлять множеством перегрзки.
Например, следующая шаблонная функция применяется только к итераторам арифметических типов. Применим boost::iterator_value, метафункцию, которая извлекает value_type итератора.
#include <iterator>
#include <boost/utility/enable_if.hpp>
#include <boost/type_traits/is_arithmetic.hpp>
#include <boost/iterator/iterator_traits.hpp>
template <class Iterator>
typename boost::enable_if<
boost::is_arithmetic< // enabling condition
typename boost::iterator_value<Iterator>::type
>,
typename boost::iterator_value<Iterator>::type // return type
>::type
sum(Iterator start, Iterator end)
{
typename boost::iterator_value<Iterator>::type x(0);
for(; start != end; ++start) x += *start;
return x;
}
Если «::value» условие С - правда, то enable_if <C,T>::type будет T, поэтому функция sum просто возвращает объект value_type итератора. В противном случае, sum просто исчезает из процесса разрешения перегрузки!
Теперь, если вызвать данную функцию с не арифметическим итератором, то компилятор сообщит, что не существует ни одной функции соответствующей вызову.
Если просто написать:
std::iterator_traits<Iterator>::value_type
вместо enable_if <...>::type, то вызывая sum с итераторами, чей value_type явялется std::vector<int> - приведет к ошибке в работе sum, при поптыке использовать оператор «+=». Если value_type итераторов являлся бы бы std::string, то компиляция скорее всего выполнится, но в будущем возможно неопределенное поведение работы функции.
Так, можно самостоятельно организовать допустимое множество для перегрузки. Например, добавить к уже имеющемуся, возможность вызова функции с типом итератора vector<vector<int>> и других вложенных контейнеров арифметических типов.
//+--------------------------------------------------------------------+
// given an Iterator that points to a container, get the
// value_type of that container's iterators.
template <class Iterator>
struct inner_value
: boost::iterator_value<
typename boost::iterator_value<Iterator>::type::iterator
>
{};
//+--------------------------------------------------------------------+
template <class Iterator>
typename boost::lazy_disable_if<
boost::is_arithmetic< // disabling condition
typename boost::iterator_value<Iterator>::type
>,
inner_value<Iterator> // result metafunction
>::type
sum(Iterator start, Iterator end)
{
typename inner_value<Iterator>::type x(0);
for (; start != end; ++start)
x += sum(start->begin(), start->end());
return x;
}
Слово «disable» в lazy_disable_if означает, что функция будет удалена из множества перегрузки устанавливается, если условие дает правду. Слово «lazy» означает, что элемент «::type» явялется результатом вызова второго аргумента, как нульарной метафункции.
Для полноты: enable_if.hpp содержит шаблоны disable_if и lazy_enable_if, версии с суффиксом «_C» всех четырех шаблонов, принимают целочисленные константы вместо обертки, в качестве первого аргумента.
Обратите внимание, что inner_value <Iterator> может быть вызван только, если значение типа Iterator является еще одним итератором. В противном случае, будет получена ошибка.
Определение enable_if:
template <bool, class T = void>
struct enable_if_c
{
typedef T type;
};
template <class T>
struct enable_if_c<false, T>
{};
template <class Cond, class T = void>
struct enable_if
: enable_if_c<Cond::value, T>
{};
Когда условие является ложным, enable_if_c<C,T>::type не существует! Стандарт C++ касательно правил разрешения перегрузки говорят, что, когда вывод аргумента функции шаблона не удается, он не вносит ничего в набор кандидатов рассматриваемых функций для данного вызова и такой вызов не является ошибкой. Этот принцип называется «неверная подстановка не является ошибкой» «Substitution Failure Is Not An Error» (SFINAE), Дэвид Вандердвуд и Николай Джосьютис [VJ02].
Причина использования inner_value и ленивых вычислений: несмотря на то, что enable_if не приведит к ошибке. Правила вывода аргументов шаблона (14.8.2, пункт 2), перечисляют условия, при которых неудачный вывод аргументов шаблона является ошибкой. Оказывается, что форма enable_if находится в этом списке, но ошибка возможна только во время инчтанцирования шаблонной функции (например, iterator_value), а не во время вывода, потому ленивые вычисления помогают использовать этот факт.
sizeof
Несмотря на то, что значения, используемые в качестве аргументов функций передаются во время выполнения переходят в мире среда постоянно, можно получить некоторую информацию во время компиляции типе возвращаемого значения из функции с помощью оператора sizeof.
Эта техника лежит в основе многочисленных низкоуровневых шаблонных метафункций, в том числе и в Boost TypeTraits Library.
Например можно написать свойство, отделяющая классы и объединения от всех других типов:
typedef char yes; // sizeof(yes) == 1
typedef char (&no)[2]; // sizeof(no) == 2
//+--------------------------------------------------------------------+
template <class T>
struct is_class_or_union
{
// SFINAE eliminates this when the type of arg is invalid
template <class U>
static yes tester(int U::*arg);
// overload resolution prefers anything at all over "..."
template <class U>
static no tester(...);
// see which overload is chosen when U == T
static bool const value
= sizeof(tester<T>(0)) == sizeof(yes);
typedef mpl::bool_<value> type;
};
struct X{};
BOOST_STATIC_ASSERT(is_class_or_union<X>::value);
BOOST_STATIC_ASSERT(!is_class_or_union<int>::value);
Очень ограниченным есть тот факт, что получить можно только размер типа, а не сам тип по время выполнения. Например, было бы хорошо иметь возможность писать так:
// generalized addition function object
struct add
{
template <class T, class U>
typeof(T+U) operator()(T const& t, U const& u)
{
return t+u;
}
};
