c++c++20range-v3

Create a custom transformable view in ranges-v3


I want to create my own view using the ranges-v3 library. One should be able to pipe this view into ranges::views::transform(...) without problem, alas my current approach does not seem to allow this.

The scenario:
I have made a function that does nothing more than dereference the input and perfect-returns it, like so:

template < typename T >
decltype(auto) deref(T&& t)
{
   return std::forward< T >(t);
}

template < typename T >
   requires std::is_pointer_v< std::remove_cvref_t< T > > or other_pointer_types_like_smart_pointers...
decltype(auto) deref(T&& t)
{
      return *std::forward< T >(t);
}

Here my custom view which wraps this function:

template < ranges::range Range >
class deref_view: public ranges::view_base {
  public:
   struct iterator;
   deref_view() = default;
   deref_view(ranges::range auto&& base) : m_base(base) {}

   iterator begin() { return ranges::begin(m_base); }
   iterator end() { return ranges::end(m_base); }

  private:
   Range m_base;
};

template < ranges::range Range >
struct deref_view< Range >::iterator: ranges::iterator_t< Range > {
   using base = ranges::iterator_t< Range >;

   iterator() = default;

   iterator(const base& b) : base{b} {}

   iterator operator++(int) { return static_cast< base& >(*this)++; }

   iterator& operator++()
   {
      ++static_cast< base& >(*this);
      return (*this);
   }

   decltype(auto) operator*() const { return deref(*static_cast< base >(*this)); }
};

template < ranges::range Range >
deref_view(Range&&) -> deref_view< ranges::cpp20::views::all_t< Range > >;

struct deref_fn {
   template < typename Rng >
   auto operator()(Rng&& rng) const
   {
      return deref_view{ranges::views::all(std::forward< Rng >(rng))};
   }

   template < typename Rng >
   friend auto operator|(Rng&& rng, deref_fn const&)
   {
      return deref_view{ranges::views::all(std::forward< Rng >(rng))};
   }
};

namespace ranges::views {

constexpr deref_fn deref{};

}  

Now I am using it on a simple vector of shared pointers to int and it works fine

std::vector<std::shared_ptr<int> > vec{
   std::make_unique<int>(0),
   std::make_unique<int>(1), 
   std::make_unique<int>(2)
};

for (auto elem : vec | ranges::views::deref) {
    std::cout << elem << std::endl;
}

prints

0
1
2

But piping it into a transform fails:

for (auto elem : vec | ranges::views::deref | ranges::views::transform([](const auto& in) { return in; })) {
    std::cout << elem << std::endl;
}

As can be seen on compiler-explorer here:

https://godbolt.org/z/1KWMb34e4

Any advice on what I am doing wrong would be greatly appreciated!


Solution

  • input_iterator requires the member type alias value_type and difference_type to exist, so you need to add it to deref_view::iterator:

    template <ranges::range Range>
    struct deref_view<Range>::iterator : ranges::iterator_t<Range> {
        using base = ranges::iterator_t<Range>;
        using value_type = // some metaprogramming
        using difference_type = ranges::range_difference_t<Range>;
        // ...
    };