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

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

Но это еще не совсем решение. Попытка применить алгоритм transform выполненяя operator* приводит к ошибке компиляции:

#include <boost/mpl/transform.hpp>

template <class T, class D1, class D2>

quantity

<

T,

typename mpl::transform<D1,D2,mpl::plus>::type // нужен полноценный тип, а не имя шаблона

>

operator*(quantity<T, D1> x, quantity<T, D2> y)

{

//...

}

Причина ошибки в том, что протокол говорит, что аргументы метафункции должны быть типами, а plus не тип, а шаблон класса plus<T1, T2>.

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

struct plus_f

{

template <class T1, class T2>

struct apply

{

typedef typename mpl::plus<T1,T2>::type type;

};

};

Класс метафункции (метафункциональный класс) - класс с вложенной метафункцией, доступ к которой открыт.

Метафункция - шаблон, а не тип, но класс метафункции, который обертывает такой шаблон в теле обычного нешаблонного класса – является простым типом.

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

Теперь BinaryOperation может быть выполнена:

template <class T, class D1, class D2>

quantity

<

T,

typename mpl::transform<D1,D2,plus_f>::type // new dimensions

>

operator*(quantity<T, D1> x, quantity<T, D2> y)

{

typedef typename mpl::transform<D1, D2, plus_f>::type dim;

return quantity<T,dim>( x.value() * y.value() );

}

Теперь, если мы хотим вычислить силу тяжести действующую на пятикилограммовом ноутбуке, это всего лишь ускорение силы тяжести (9.8 m/sec2), вызванной массой ноутбука (умножение):

quantity<float, mass> m(5.0f);

quantity<float, acceleration> a(9.8f);

cout << "force = " << (m * a).value();

Разбор:

typedef mpl::vector_c<int,1,0,0,0,0,0,0> mass;

typedef mpl::vector_c<int,0,1,-2,0,0,0,0> acceleration; // l/(t2)

typedef typename mpl::transform<D1,D2,plus_f>::type dim;

mpl::transform<mass, acceleration, plus_f>::type dim;

1,0, 0,0,0,0,0

0,1,-2,0,0,0,0

+-----------------

1,1,-2,0,0,0,0 // Результирующая последовательность

dim == mpl::vector_c<int,1,1,-2,0,0,0,0>; // Новое размерность: сила тяжести ноутбука в 5 кг

Так, operator* умножает величины во время выполнения (приводящий к 6.0f), и наше кодовое использование метапрограммы преобразовывает метапоследовательности фундаментальных показателей степени так, чтобы тип результата содержал новый список показателей степени:

vector_c<int,1,1,-2,0,0,0,0>;

Но, попытка записать следующее выражение приведет к проблемам:

quantity<float,force> f = m * a;

Хотя результат м * a действительно представляет силу с показателями степени массы, длины, и время как 1, 1, и -2 соответственно, тип, возвращенный преобразованием, не является специализацией vector_c. Вместо этого алгоритм вернет новую последовательность с соответствующими элементами: vector_c <int, 1,1,-2,0,0,0,0>. Дело в том, что quantity<float,force> является другим типом, что и приводит к ошибке.

Решение 1: добавим неявное преобразование результата умножения к типу quantity<float, force> . Так как мы не можем предсказать точные типы размерностей в любом вычислении, то преобразование должно быть шаблонным:

template <class T, class Dimensions>

struct quantity

{

// converting constructor

template <class OtherDimensions>

quantity(quantity<T,OtherDimensions> const& rhs)

: m_value(rhs.value())

{}

//...

}

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

// Should yield a force, not a mass!

quantity<float, mass> bogus = m * a;

Решение 2: использование дргуого алгоритма MPL – equal, который позволит проверять идентичность элементов двух последовательностей и выполнить преобразование только в случае их равенства:

template <class OtherDimensions>

quantity(quantity<T,OtherDimensions> const& rhs) : m_value(rhs.value())

{

BOOST_STATIC_ASSERT((

mpl::equal<Dimensions,OtherDimensions>::type::value

));

}

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