Extending return type deduction system
В этом разделе мы объясним, как расширить систему вычета возвратного типа для охвата операторов, определенных пользователем. Во многих случаях в этом нет необходимости, поскольку BLL определяет типы возврата по умолчанию для операторов. Например, тип возврата по умолчанию для всех операторов сравнения равен<bool
>, и до тех пор, пока определяемые пользователем операторы сравнения имеют тип возврата bool, нет необходимости писать новые специализации для классов вычета типа возврата. Однако иногда этого нельзя избежать.
Перегружаемые операторы, определенные пользователем, являются унарными или двоичными. Для каждого района есть два шаблона признаков, которые определяют типы возврата различных операторов. Следовательно, система возвратного типа может быть расширена за счет предоставления дополнительных специализаций для этих шаблонов. Шаблоны для унарных функторов<
plain_return_type_1<Action, A>
>и<
return_type_1<Action, A>
>и<
plain_return_type_2<Action, A, B>
>и<
return_type_2<Action, A, B>
>соответственно для бинарных функторов.
Первым параметром (<Action
>) ко всем этим шаблонам является классдействия, который определяет оператора. Операторы с аналогичными правилами возврата группируются вместе вгруппы действий, и только класс действий и группа действий вместе определяют оператора однозначно. В качестве примера, тип действия<arithmetic_action<plus_action>
>означает<operator+
>. Полный список различных типов действий приведен вТаблица 18.2, “ Типы действий”.
Последние параметры<A
>в унарном случае или<A
>и<B
>в двоичном случае обозначают типы аргументов вызова оператора. Два набора шаблонов<plain_return_type_n
>
и<return_type_n
>
<n
>[1 или 2] отличаются тем, как им представлены типы параметров. Для предыдущих шаблонов типы параметров всегда предоставляются в качестве нессылочных типов и не имеют конст- или летучих квалификаторов. Это облегчает специализацию, поскольку обычно достаточно одной специализации для каждого оператора, определенного пользователем, или группы операторов. С другой стороны, если конкретный оператор перегружен для разных квалификаций одного и того же типа аргументов, а типы возврата этих перегруженных версий отличаются, требуется более тонкое управление. Следовательно, для последних шаблонов типы параметров сохраняют cv-квалификаторы, а также являются нессылочными типами. Недостатком является то, что для перегруженного набора операторов, описанных выше, может потребоваться до 16<return_type_2
>специализаций.
Предположим, что пользователь перегрузил следующие операторы для некоторых определенных пользователем типов<X
>,<Y
>и<Z
>:
Z operator+(const X&, const Y&);
Z operator-(const X&, const Y&);
Теперь можно добавить специализацию, утверждающую, что если аргумент левой руки относится к типу<X
>, а правой — к типу<Y
>, то тип возврата всех таких бинарных арифметических операторов равен<Z
>:
namespace boost {
namespace lambda {
template<class Act>
struct plain_return_type_2<arithmetic_action<Act>, X, Y> {
typedef Z type;
};
}
}
Определив эту специализацию, BLL может правильно определить тип возврата вышеупомянутых двух операторов. Обратите внимание, что специализации должны быть в одном и том же пространстве имен<::boost::lambda
>с основным шаблоном. Для краткости мы не показываем определения пространства имен в примерах ниже.
Можно специализироваться и на уровне отдельного оператора, помимо предоставления специализации для группы операторов. Скажем, мы добавим новый оператор арифметики для типов аргументов<X
>и<Y
>:
X operator*(const X&, const Y&);
Наше первое правило для всех арифметических операторов указывает, что тип возврата этого оператора<Z
>, что, очевидно, не так. Таким образом, мы предлагаем новое правило для оператора умножения:
template<>
struct plain_return_type_2<arithmetic_action<multiply_action>, X, Y> {
typedef X type;
};
Специализации могут определять произвольные отображения от типов аргументов к типу возврата. Предположим, что у нас есть некоторый математический векторный тип, шаблонизированный по типу элемента:
template <class T> class my_vector;
Предположим, что оператор сложения определяется между любыми двумя<my_vector
>инстанциями, если оператор сложения определен между их типами элементов. Кроме того, тип элемента полученного<my_vector
>является таким же, как тип результата добавления между типами элементов.<my_vector<int>
>,<my_vector<double>
>,<my_vector<double>
>,<my_vector<double>
>,<my_vector<double>
>. BLL имеет классы признаков для выполнения неявных встроенных и стандартных преобразований типа между интегральными, плавающими точками и сложными классами. Используя инструменты BLL, оператор добавления, описанный выше, может быть определен как:
template<class A, class B>
my_vector<typename return_type_2<arithmetic_action<plus_action>, A, B>::type>
operator+(const my_vector<A>& a, const my_vector<B>& b)
{
typedef typename
return_type_2<arithmetic_action<plus_action>, A, B>::type res_type;
return my_vector<res_type>();
}
Чтобы BLL мог правильно определить тип<my_vector
>добавлений, мы можем определить:
template<class A, class B>
class plain_return_type_2<arithmetic_action<plus_action>,
my_vector<A>, my_vector<B> > {
typedef typename
return_type_2<arithmetic_action<plus_action>, A, B>::type res_type;
public:
typedef my_vector<res_type> type;
};
Обратите внимание, что мы повторно используем существующие специализации для шаблона BLL<return_type_2
>, которые требуют, чтобы типы аргументов были ссылками.
Table 18.2. Action types
<+ > | <arithmetic_action<plus_action> > |
<- > | <arithmetic_action<minus_action> > |
<* > | <arithmetic_action<multiply_action> > |
</ > | <arithmetic_action<divide_action> > |
<% > | <arithmetic_action<remainder_action> > |
<+ > | <unary_arithmetic_action<plus_action> > |
<- > | <unary_arithmetic_action<minus_action> > |
<& > | <bitwise_action<and_action> > |
<| > | <bitwise_action<or_action> > |
<~ > | <bitwise_action<not_action> > |
<~ > | <bitwise_action<xor_action> > |
<<< > | <bitwise_action<leftshift_action_no_stream> > |
<>> > | <bitwise_action<rightshift_action_no_stream> > |
<&& > | <logical_action<and_action> > |
<|| > | <logical_action<or_action> > |
<! > | <logical_action<not_action> > |
<< > | <relational_action<less_action> > |
<> > | <relational_action<greater_action> > |
<<= > | <relational_action<lessorequal_action> > |
<>= > | <relational_action<greaterorequal_action> > |
<== > | <relational_action<equal_action> > |
<!= > | <relational_action<notequal_action> > |
<+= > | <arithmetic_assignment_action<plus_action> > |
<-= > | <arithmetic_assignment_action<minus_action> > |
<*= > | <arithmetic_assignment_action<multiply_action> > |
</= > | <arithmetic_assignment_action<divide_action> > |
<%= > | <arithmetic_assignment_action<remainder_action> > |
<&= > | <bitwise_assignment_action<and_action> > |
<=| > | <bitwise_assignment_action<or_action> > |
<^= > | <bitwise_assignment_action<xor_action> > |
<<<= > | <bitwise_assignment_action<leftshift_action> > |
<>>= > | <bitwise_assignment_action<rightshift_action> > |
<++ > | <pre_increment_decrement_action<increment_action> > |
<- > | <pre_increment_decrement_action<decrement_action> > |
<++ > | <post_increment_decrement_action<increment_action> > |
<- > | <post_increment_decrement_action<decrement_action> > |
<& > | <other_action<address_of_action> > |
<* > | <other_action<contents_of_action> > |
<, > | <other_action<comma_action> > |
<->* > | <other_action<member_pointer_action> > |