I want to implement some generic algorithms and I have a number of ideas how specialized algorithms could be implemented depending on certain traits of entities the algorithm is used with. However, it seems likely that I didn't come up with all special traits and I'd like to implement the generic version such that they could work with another specialized version.
For example, consider distance(begin, end)
(yes, I know it is in the standad library; however, it is nice and simple and can be used to demonstrate my problem). A general version could look like this (I'm using std::ptrdiff_t
instead of std::iterator_traits<It>::difference_type
as another simplification):
template <typename It>
auto distance(It it, It end) -> std::ptrdiff_t {
std::ptrdiff_t size{};
while (it != end) {
++it;
++size;
}
return size;
}
Of course, if the iterator type is a random access iterator, it is much better to implement the algorithm using the difference between the two iterators. Naively just adding
template <typename It>
auto distance(It begin, It end)
-> typename std::enable_if<is_random_access_v<It>, std::ptrdiff_t>::type {
return end - begin;
}
doesn't quite work: both implementation are equally good matches for random access iterators, i.e., the compiler considers them to be ambiguous. The easy approach to deal with the situation is to change the general implementation to be applicable only for non random access iterators. That is, the SFINAE choices are made such that they are mutually exclusive while also covering the entire space.
Unfortunately, the set of implementations is still closed: without changing the signature of, at least, one of the implementations I can't add another implementation in case I have another idea for a generic implementation taking advantage of special properties. For example, if I want to add special processing for segmented ranges (idea: when the underlying sequence consists of segments as is, e.g., the case for std::deque<...>
or std::istreambuf_iterator<cT>
, process segments individually) it would be necessary to change the general implementation to be applicable only when the sequences isn't a random access and it isn't a segmented sequence. Of course, if I control the implementation that can be done. A user wouldn't be able to extend the set of generic implementations.
I am aware that the functions can be overloaded for special iterator types. However, that would require that every time an iterator with special capabilities is added it would need to implement the respective functions. The goal is to enable adding generic implementations which areimprovements in case the entities they are used with expose additional facilities. It is similar to the different iterator categories, although the properties are orthogonal to the iterator categories.
Thus, my question is:
- Can generic algorithms be implemented such that new improvement idea can be added without changing the existing implementations and, if so, how?
- Optional follow-up (I'm primarily interested in the question above but this one could be interesting, too): If that isn't possible, would this ability be added with concepts?