Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to pass a smart iterator to a function that accepts a classic iterator?

I'm trying to familiarize with the ranges-v3 library, that will be part of the C++20 standard. To do so, I'm trying to refactor some toy code by replacing (where suitable) classic iterators and algorithms with the new available constructs. In this particular example, I can't figure out how to pass the returned iterator of a call to ranges::min_element (which replaced a call to std::min_element) to another function of mine which accepts a classic iterator as parameter.

I've searched inside the documentation of the library looking for some sort of functions like smartIt2classicIt with no success.

Here is a minimal example

void f(std::vector<int>& v, std::vector<int>::iterator it); // old function that I want to reuse
auto predicate = [](int i){ return true; }; // check function

std::vector<int> v;
// auto min_el = std::min_element(...); // old code
auto filtered_range = v | ranges::view::filter(predicate); // to avoid a dangling iterator
auto min_el = ranges::min_element(filtered_range);

f(v, min_el); // pass min_el to f: doesn't compile with the new code

At first I expected that the result of ranges::min_element was implicitly convertible to a classic iterator, but I was wrong: the compiler returns a long error saying that cannot convert a ranges::basic_iterator bla bla bla to a std::vector bla bla bla iterator. Given this error I deduce that ranges::min_element indeed returns some sort of iterator, but how to use it the old way?

I see three possible solutions:

  1. Change the way min_el is passed to f
  2. Change the type of the second argument of f (possibly in a backward-compatible way)
  3. Change both things

but I can't figure out how to implement any of them. Maybe there is another solution? I also see another possible source of troubles, since the returned iterator probably refers to filtered_range instead of v... Any help is welcome!

like image 699
Rackbox Avatar asked Aug 01 '19 13:08

Rackbox


People also ask

Can we increment iterator c++?

Iterator: a pointer-like object that can be incremented with ++, dereferenced with *, and compared against another iterator with != . Iterators are generated by STL container member functions, such as begin() and end().

Which function is used to get a move iterator from a container?

Generally, like this: struct vec{ iterator begin() ; const_iterator begin() const; };

How do I assign an iterator in C++?

Operator= -- Assign the iterator to a new position (typically the start or end of the container's elements). To assign the value of the element the iterator is pointing at, dereference the iterator first, then use the assign operator.

When to use iterators in c++?

An iterator is used to point to the memory address of the STL container classes. For better understanding, you can relate them with a pointer, to some extent. Iterators act as a bridge that connects algorithms to STL containers and allows the modifications of the data present inside the container.


1 Answers

You can convert an adapted iterator to its base iterator via the base() member function.

For example:

std::vector<int>::const_iterator foo(std::vector<int> const& v) {
    auto filtered = v | ranges::view::filter([](int i){return i > 5;});
    auto min_el = ranges::min_element(filtered);
    return min_el.base();
}

Note that this only removes one layer of wrapping, it doesn't drop to the bottom. So if you had another adapter then you'd need another base():

std::vector<int>::const_iterator foo(std::vector<int> const& v) {
    auto filtered = v | ranges::view::filter([](int i){return i > 5;})
                      | ranges::view::transform([](int i){ return i * i; });
    auto min_el = ranges::min_element(filtered);
    return min_el.base().base();
}
like image 137
Barry Avatar answered Sep 24 '22 03:09

Barry