最終更新日時:
が更新

履歴 編集

6. 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) は、演算子を指定している アクション クラスである。 同じ返り値の型の規則を持った演算子は、 アクショングループ にまとめられ、アクションクラスとアクショングループのみが演算子の曖昧さを解決する。 例を挙げると、operator+を示すアクション型 arithmetic_action<plus_action> がある。 他のアクション型の完全なリストを Table 2 に示す。

単項関数の場合の A や二項関数の場合の AB の残りのテンプレート引数は、演算子呼出しの引数の型を示している。 二つのテンプレート群 plain_return_type_nreturn_type_n は、(n は1または2) 引数の型の提示の仕方において異なる。 前者のテンプレートでは、引数の型は常に参照型でなく、また、const や volatile といった修飾子もついていない。 これにより、一般的に各ユーザ定義の演算子や演算子群ごとに一つの特殊化を行うだけで十分となるため、特殊化が簡単になる。 一方、特定の演算子を同じ型の引数に異なった const/volatile の修飾子をつけたり、オーバーロードした演算子の返り値の型が異なる場合には、より細かい操作が必要となる。 よって、後者のテンプレートでは、引数の型は const/volatile の修飾子も変更せず、非参照型もそのままにする。 欠点は上記のようなオーバーロードした演算子のために、最悪の場合十六個の return_type_2 の特殊化が必要となるかもしれないことである。

ユーザ定義の型 XYZ に対して演算子をオーバーロードしたとする。

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に存在しなければならない。 簡単のために、以下の例ではこの名前空間の定義は省略する。

演算子グループの特殊化に加えて、個々の演算子ごとに特殊化を行うことも可能である。 例えば、新しく引数の型が、XY である算術演算子を追加したとする。

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 の要素の加法の結果の型と同じであるとする。 例えば、my_vector<int>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>();
}

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 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>