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

Ленивый Выбор Типа

Подход с инстанцированием add_reference <T const> явялется неэффективным, т.к. будет всегда вызываться даже если аргумент имеет тип скаляра. Есть способ отложить инстанцирование add_reference до реальной необходимости – это позволить mpl::if_ делать выбор из двух нульарных метафункций, в результате чего инстанцироваться будет только выбранная:

#include <boost/mpl/if.hpp>

#include <boost/mpl/identity.hpp>

#include <boost/type_traits/add_reference.hpp>

template <class T>

struct param_type

: mpl::if_ // forwarding to selected transformation

<

typename boost::is_scalar<T>::type,

mpl::identity<T>,

boost::add_reference<T const>

>::type

{};

mpl::identity - метафункция, которая возвращает свой аргумент. Теперь param_type<T> возвращает результат вызова любого mpl::identity <T> или add_reference <T const>, в зависимости от того, является ли T скаляром. Эта идиома настолько распространена в метапрограммах, что MPL создает для нее отдельную метафункцию, названную eval_if:

template <class C, class TrueMetafunc, class FalseMetafunc>

struct eval_if

: mpl::if_<C,TrueMetafunc,FalseMetafunc>::type

{};

Принимая во внимание, что if_ возвращает один из двух аргументов, основанных на условии, eval_if вызывает один из двух аргументов нульарной метафункции, согласно условию и возвращает результат.

Теперь можно немного упростить определение param_type, с помощью eval_if:

#include <boost/mpl/eval_if.hpp>

#include <boost/mpl/identity.hpp>

#include <boost/type_traits/add_reference.hpp>

template <class T>

struct param_type

: mpl::eval_if

<

typename boost::is_scalar<T>::type,

mpl::identity<T>,

boost::add_reference<T const>

> // no ::type here

{};

Используя тот факт, что все целочисленные метацфункции содержат элемент «::value», может написать следующее:

template <class T>

struct param_type

: mpl::eval_if

<

boost::is_scalar<T>, //! Неявное преобразование к вложенному типу

mpl::identity<T>,

boost::add_reference<T const>

>

{};

Специализации метафункций таких как is_scalar, возвращают константные целочисленные обертки публично полученные от внешней обертки, а также элементы наследуемого базового класса, например: bool_:

if(boost::is_scalar<X>()) // invokes inherited operator bool()

{

// code here runs iff X is a scalar type

}

Логические Операторы

Если бы не было механизма наподобии add_reference или он был бы определен как показано ниже, то мы не могли бы избежать возможного возникновения ссылки на ссылку:

template <class T>

struct add_reference { typedef T& type; };

В этом случае пришлось бы переделывать param_type подобно следующей конструкции:

template <class T>

struct param_type

: mpl::eval_if

<

mpl::bool_

<

boost::is_scalar<T>::value ||

boost::is_reference<T>::value

>,

mpl::identity<T>,

add_reference<T const>

>

{};

Большая часть синтаксической чистоты нашей предыдущей версии была потеряна. Если бы мы хотели построить лямбда выражение для этой версии param_type, то возникли бы еще более серьезные проблемы:

typedef mpl::vector<int, long, std::string> argument_types;

// build a list of parameter types for the argument types

typedef mpl::transform

<

argument_types,

mpl::if_

<

mpl::bool_

<

boost::is_scalar<_1>::value ||

boost::is_reference<_1>::value

>,

mpl::identity<_1>,

add_reference<boost::add_const<_1>

>

>::type param_types;

Это уже фактически не в состоянии работать должным образом: логическое выражения:: is_scalar <_1>::value || is_reference <_1>::value будут вычисляться даже если «_1» не является скаляром и ссылкой. Можно решить обе эти проблемы, используя логические метафункции MPL. Решение: добавление и в этот процесс дополнительного уровня абстракции в виде метафункции mpl::or_, что позволит вернуть синтаксическую чистоту оригинального param_type:

#include <boost/mpl/or.hpp>

template <class T>

struct param_type

: mpl::eval_if

<

mpl::or_ // будет инстанцирована

<

boost::is_scalar<T>,

boost::is_reference<T>

>,

mpl::identity<T>,

add_reference<T const>

>

{};

Поскольку mpl::or_ <x, y> выводит тип «::type»(специализация bool _<n>), что явялется допустимой булевой константой оберткой в MPL, мы в состоянии полностью устранить явное использование bool_ и доступа к вложенному «::type».

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

Эта выгода усиливается при использовании такого подхода в лямбда выражениях:

typedef mpl::transform

<

argument_types,

mpl::if_

<

mpl::or_

<

boost::is_scalar<_1>,

boost::is_reference<_1>

>,

mpl::identity<_1>,

add_reference<boost::add_const<_1>

>

>::type param_types;

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

#include <boost/type_traits/is_stateless.hpp>

template <class T>

struct param_type

: mpl::eval_if

<

mpl::or_

<

boost::is_stateless<T>,

mpl::or_

<

boost::is_scalar<T>,

boost::is_reference<T>

>

>,

mpl::identity<T>,

add_reference<T const>

>

{};

Это работает, но можно сделать этот код более читабельным, т.к. mpl::or_ способен принимать от двух до пяти аргументов:

#include <boost/type_traits/is_stateless.hpp>

template <class T>

struct param_type

: mpl::eval_if

<

mpl::or_

<

boost::is_scalar<T>,

boost::is_stateless<T>,

boost::is_reference<T>

>,

mpl::identity<T>,

add_reference<T const>

>

{};

Фактически, большинство метафункций MPL, которые опрерируют целочисленными константными аргументами (например, mpl::plus<...>), имеют то же самое свойство.

Библиотека содержит подобную логическую метафункцию and_, и унарную метафункцию not_ для того, чтобы инвертировать булевые условия. Это работает так же как встроенные операторы «&&» и «||», mpl::and_ и mpl::or_ - это прямая деонстрация поведения «короткого замыкания».

Например, в примере выше, если T - скаляр, is_stateless <T> и is_reference <T> не будут инстанцироваться.

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