ConceptC++ has long had a shortcut for specifying the constraints on a template type parameter by a single-type concept. Instead of writing such a requirement in a where clause, e.g.,
template<typename Iter> where InputIterator<Iter>
InputIterator<Iter>::difference_type distance(Iter first, Iter last);
one can instead write the template header as if InputIterator were the “type” of Iter, e.g.,
template<InputIterator Iter>
InputIterator<Iter>::difference_type distance(Iter first, Iter last);
This shortcut syntax actually adds one new feature that simplifies the use of associated types. When declaring a template type parameter as InputIterator Iter, all of the associated types of InputIterator also become available as nested types of Iter. Thus, for instance, Iter::value_type becomes an alias of InputIterator<Iter>::value_type, allowing further simplification of our distance() function:
template<InputIterator Iter>
Iter::difference_type distance(Iter first, Iter last);
Although this shortcut has been available for single-type concepts for quite awhile, it was only recently that we considered making them available for multi-type concepts. For me, the use of the shortcut went from excessive to crucial once I realized that we could eliminate a great deal of redundancy in certain where clauses due to the injection of associated types. For instance, here is the declaration of inner_product without using the shortcut anywhere:
template<typename Iter1, typename Iter2, typename T,
typename BinaryOperation1, typename BinaryOperation2>
where InputIterator<Iter1> && InputIterator<Iter2> &&
Callable2<BinaryOperation2,
InputIterator<Iter1>::reference,
InputIterator<Iter2>::reference> &&
Callable2<
BinaryOperation1,
T,
Callable2<
BinaryOperation2,
InputIterator<Iter1>::reference,
InputIterator<Iter2>::reference
>::result_type
> &&
Assignable<
T,
Callable2<
BinaryOperation1,
T,
Callable2<
BinaryOperation2,
InputIterator<Iter1>::reference,
InputIterator<Iter2>::reference
>::result_type
>::result_type
>
T
inner_product(Iter1 first1, Iter1 last1,
Iter2 first2, T init,
BinaryOperation1 binary_op1,
BinaryOperation2 binary_op2)
Yikes! Using the shortcut for single-type concepts leads to some improvement in the where clause:
template<InputIterator Iter1, InputIterator Iter2, typename T,
typename BinaryOperation1, typename BinaryOperation2>
where Callable2<BinaryOperation2, Iter1::reference, Iter2::reference> &&
Callable2<
BinaryOperation1,
T,
Callable2<BinaryOperation2, Iter1::reference, Iter2::reference>::result_type
> &&
Assignable<
T,
Callable2<
BinaryOperation1,
T,
Callable2<BinaryOperation2, Iter1::reference, Iter2::reference>::result_type
>::result_type
>
T
inner_product(Iter1 first1, Iter1 last1,
Iter2 first2, T init,
BinaryOperation1 binary_op1,
BinaryOperation2 binary_op2)
Finally, using the shortcut for multi-type concepts we can simplify a bit further:
template<InputIterator Iter1, InputIterator Iter2, typename T,
typename BinaryOperation1,
Callable2<Iter1::reference, Iter2::reference> BinaryOperation2>
where Callable2<BinaryOperation1, T, BinaryOperation2::result_type>&&
Assignable<
T,
Callable2<BinaryOperation1, T, BinaryOperation2::result_type>::result_type
>
T
inner_product(Iter1 first1, Iter1 last1,
Iter2 first2, T init,
BinaryOperation1 binary_op1,
BinaryOperation2 binary_op2)
With templates from the Standard Library, we are not allowed to reorder the template parameters because it would break backward compatibility. If we could reorder the template parameters, however, one could swap BinaryOperation1 and BinaryOperation2 in the template parameter list, so that the remaining Callable2 requirement in the where clause could be moved into the template header. Still, even by shuffling the template parameters, we cannot eliminate the need for the where clause entirely: there are too many interdependencies among the template parameters. So, while extending the “type-of-type” syntax can help us drastically shorten the statement of requirements for a template, it cannot eliminate the need for where clauses entirely.
The latest development version of ConceptGCC supports the “type-of-type” shortcut for multi-type concepts. The implementation of this feature was trivial, posing no interesting challenges. An audit of the algorithms in Chapter 25 of the C++ Standard showed that use of this feature shortened and simplified concept constraints for quite a few algorithms, particularly those that relied on the OutputIterator, BinaryPredicate, and Callable2 concepts.