2008-10-09

コンセプトの名前付き要求

n2581 Named Requirements for C++0X Concepts
n2780 Named Requirements for C++0X Concepts, version 2

私は、コンセプトを使うのは簡単であると、一度も言ったことがない。寝言で言ったかもしれないが、それは所詮寝言だ。たとえば、STLのsortに使えるイテレータを持ったrangeを扱う場合には、以下のように記述する必要がある。

template < typename T >
    requires std::Range< T >
       && std::RandomAccessIterator< std::Range< T >::iterator >
       && std::ShuffleIterator< std::Range< T >::iterator >
       && std::LessThanComparable< std::Range< T >::iterator::value_type >
void f(T & r)
{
    std::sort( begin(r), end(r) ) ;
}

もちろん、すべてをrequires-clauseに記述するのではなく、constrained-template-parameter(テンプレート引数のtypename/classの場所)にRangeを指定すれば、この重複を省くことができる。しかし、constrained-template-parameterで指定できるコンセプトはひとつだけなので、もっと複雑になると、こうなる。

template<InputIterator Iter1, InputIterator Iter2, typename T>
    requires Multiplicable< Iter1::reference
                , Iter2::reference
               >
    && Addable< T
         , Multiplicable< Iter1::reference
                 , Iter2::reference
                 >::result_type
         >
    && Assignable< T
           , Addable< T
               , Multiplicable< Iter1::reference
                       , Iter2::reference
                       >::result_type
               >::result_type
           >
T inner_product( Iter1 first1, Iter1 last1, Iter2 first2, T init );

言うまでもなく、このコードは冗長にも程がある。そこで、named requirementが提案され、このたびドラフト規格に入った。これは文字通り名前をつける機能で、以下のようになる。

template<InputIterator Iter1, InputIterator Iter2, typename T>
    requires mult = Multiplicable<Iter1::reference, Iter2:reference>
       && add = Addable<T, mult::result_type>
       && Assignable<T, add::result_type>
T inner_product( Iter1 first1, Iter1 last1, Iter2 first2, T init );

とても分かりやすい。

これはrequires-clauseに対して適用されるので、もちろん、associated-requirements(コンセプトの中のrequires)にも適用される。

auto concept BinaryIsoFunction< typename Op, typename Elem > {
    requires std::Callable<Op, Elem, Elem>;
    requires std::Convertible< std::Callable<Op, Elem, Elem>::result_type, Elem>;
    typename result_type = std::Callable<Op, Elem, Elem>::result_type;
};

また、refinement clausesの(いわばコンセプトの継承とでも言うべき機能)、refinement-specifier でも使用できる。つまり、

concept Refining< typename T, typename U >
    : Complicated<T,U>
{
    requires std::HasPlus< Complicated<T,U>::result_type >;
} ;

ではなく、

concept Refining< typename T, typename U >
    : c = Complicated<T,U>
{
    requires std::HasPlus< c::result_type >;
} ;

とできる。

No comments: