Consider the following code:
#include <vector>
#include <array>
#include <algorithm>
#include <iterator>
template <typename Container, typename Pred>
bool foo(const Container c, Pred p) {
auto first = std::find_if(std::cend(c), std::cend(c), p);
auto last = std::find_if(
std::make_reverse_iterator(std::cend(c)),
std::make_reverse_iterator(std::cbegin(c)),
p);
return std::make_reverse_iterator(last) - first > 5;
}
Why does this refuse to compile? Shouldn’t the reverse-of-a-reverse-iterator be compatible with a regular iterator?
Notes:
- If I turn
first
into areverse_iterator
, instead of the other way around, the code will compile. But it’s not what I want to do, for reasons. - This question asks why a double-reverse iterator should not just have the original iterator’s type, which is a bigger ask.
- The problem should be the same for other containers, e.g.
std::vector
.
28
In this post I am dodging the main question, while proposing a cheap workaround:
template<typename iter> //default
struct consistent_reverse_type_nest
: std::type_identity<std::reverse_iterator<iter>> {};
template<typename iter> //special
struct consistent_reverse_type_nest
<std::reverse_iterator<iter>>
: std::type_identity<iter>> {};
template<typename Iter>
using consistent_reverse_type =
typename consistent_reverse_type_nest::type;
Now we can define the function:
tenplate <typename Iter>
constexper auto make_consistent_reverse_iterator(Iter it)
-> consistent_reverse_type<Iter>
{
if constexpr(
requires(Iter tst)
{ {tst.base()}->std::same_as<consistent_reverse_type<Iter>> }
)
return it.base();
else std::make_reverse_iterator(it);
};
Fixing <ranges>
is both simpler and harder. We just need a view CPO, but there’s no std boilerplate CRTP to define the CPO and let std define the pipe operator (operator |
).
The OP’s question triggers a more fundamental question of optimizing chained adapters, via type algebra to get a simpler final view/iterator type.