Home > database >  Any way to trick std::transform into operating on the iterator themselves?
Any way to trick std::transform into operating on the iterator themselves?

Time:01-28

So I wrote this code which won't compile. I think the reason is because std::transform, when given an iterator range such as this will operate on the type pointed to by the iterator, not the iterator itself. Is there any simple wrapper, standard lib tool, etc. to make this code work i.e. to store all the iterators of the original map into a new vector, with minimum changes required? Thanks!

#include <map>
#include <iostream>
#include <vector>

using MT = std::multimap<char, int>;
using MTI = MT::iterator;

int main()
{
    MT m;
    m.emplace('a', 1); m.emplace('a', 2); m.emplace('a', 3);
    m.emplace('b', 101);
        
    std::vector<MTI> itrs;
    std::transform(m.begin(), m.end(), std::back_inserter(itrs), [](MTI itr){
        return itr;
    });
}

Did not compile with gcc11 and clang13, C 17/20

CodePudding user response:

Is there such a wrapper? Not in the standard. But it doesn't mean you can't write one, even fairly simply.

template<typename It>
struct PassIt : It {
    It& operator*()              { return *this; }
    It const& operator*() const  { return *this; }
    PassIt & operator  ()        {   static_cast<It&>(*this); return *this; }
    PassIt operator  (int) const { return PassIt{static_cast<It&>(*this)  }; }
};

template<typename It>
PassIt(It) -> PassIt<It>;

That is just an example1 of wrapper that is a iterator of the specified template parameter type. It delegates to its base for the bookkeeping, while ensuring the the return types conform to returning the wrapped iterator itself when dereferencing.

You can use it in your example to simply copy the iterators

std::copy(PassIt{m.begin()}, PassIt{m.end()}, std::back_inserter(itrs));

See it live


(1) - It relies on std::iterator_traits deducing the correct things. As written in this example, it may not conform to all the requirements of the prescribed iterator type (in this case, we aimed at a forward iterator). If that happens, more boiler-plate will be required.

CodePudding user response:

The function you pass to std::transform and algorithms in general are supposed to use elements not iterators. You could use the key to find the iterator in the map, though thats neither efficient nor simple. Instead use a plain loop:

for (auto it = m.begin(); it != m.end();   it) itrs.push_back(it);
  •  Tags:  
  • Related