Well, especially with templates, the devil is in the details.
Let's first answer your questions:
- Yes, the template has the desired behavior, if it works. Your code expects a readable bidirectional range knowing its size which is not a raw array.
You really don't need to ask for the ranges size, compare iterators instead.
Asking for the previous position is inexcusable if you don't need it.
Instead of checking curr vs curr+1 and curr vs curr+2 in the loop and finally end-1 vs end-2 after, check begin vs begin+1 before the loop and curr vs curr+2 and curr+1 vs curr+2 in the loop
Until the Ranges TS is incorporated into the core standard we really want to only give you, making a range from an iterator-pair is uncomfortable, while getting a start- and end-iterator from a range is easy. So, go with the common interface using two iterators.
Also consider adding a specialization for single-pass-ranges. Allow requesting it manually, as it should be more efficient if dereferencing an iterator is expensive.
See the answer to question 2 about .cbegin() and co.
Calling the "
cversions" of the member-functionsof the member-functions restricts your codes generality.They aren't part of the core range-interface, being just convenience-functions for non-generic code, so not all types have them. See
std::initializer_list<T>for a common one.Even though raw arrays are perfectly fine, they lack member-functions.
In summary, always use the free functions, especially in generic code.
Using iterators instead of the indexing-operator allows you to efficiently use a type without cheap random-access, like lists, files, and so on.
The aim is to require the minimum interface you can get away with without compromising efficiency, so your algorithm is as generally useable as possible.
Some algorithms will take advantage of additional capabilities to provide more efficiency or other additional guarantees.