advance the iterator of a standard map
Asked Answered
L

4

6

The main question is already in the title: How do I advance the iterator of a standard map?

But since most people ask why I need this, I'll provide some more information: I have a class which has several maps. I have at least 2 differential equations, at least 2 sensor types (field, or dft) and at least 2 space types (volume, surface). I need to save all these things, and make a correlation between them. So I thought it would be wise, to have maps of these things, and when the things are correlated with each other, they have the same key in their maps.

For simplicity we only consider three maps.

class Reader
{
    struct MaxwellSensor
    {
        // some members...
    };
    struct FieldSensor
    {
        // some members
        uint fieldMember;
    };
    struct DFTSensor
    {
        // some members
        uint dftMember;
    };    
    std::map<uint, MaxwellSensor> maxwellSensors;
    std::map<uint, FieldSensor> fieldSensors;
    std::map<uint, DFTSensor> dftSensors;

    uint getCountOfMaxwellSensors(){
        return maxwellSensors.size();
    }

    uint getMemberForMaxwellSensorByIndex(uint index){
        // This follows later
    }

};

In the course of my programm I need to instantiate several variables of a SensorInterface. For that I need to know how many maxwell sensors I have, and then iterate through the maxwell sensors and get the members of the other sensors.

This looks like:

class MyType{
    public:
        uint member;
}

int main(int argc, const char* argv[])
{
    // some code
    Reader myReader;
    for(uint i = 0; i < myReader.countOfMaxwellSensors(); ++i)
    {
        MyType var;
        var.member = myReader.getMemberForMaxwellSensorByIndex(i);
    }
}

So the function in the reader should look like:

uint getMemberForMaxwellSensorByIndex(uint index)
{
    auto maxIt = std::advance(maxwellSensors.begin(), index);
    auto foundInFieldSensorsIt = std::find_if(fieldSensors.begin(), fieldSensors.end(), [&] (const std::pair<UInteger_T, FieldSensor>& kvp) { return kvp.first == maxIt->first; });
    auto foundInDFTSensorsIt = std::find_if(dftSensors.begin(), dftSensors.end(), [&] (const std::pair<UInteger_T, DFTSensor>& kvp) { return kvp.first == maxIt->first; });
    if(foundInFieldSensorsIt != fieldSensors.end())
        return fieldSensors[maxIt->first].fieldMember;
    else if(foundInDFTSensorsIt != dftSensors.end())
        return dftSensors[maxIt->first].fieldMember;
    else
    {
        std::cerr << "something went wrong." << std::endl;
        return 0;
    }
}

So... This is the intention for std::advance(maxwellSensors.begin(), index); But this doesn't compile with this error code:

error: 
  no matching function for call to 'advance'
        auto maxIt = std::advance(maxwellSensors.begin(), index);
                     ^~~~~~~~~~~~
/c++/4.6/bits/stl_iterator_base_funcs.h:171:5: note: 
  candidate function [with _InputIterator = std::_Rb_tree_iterator<std::pair<const
  unsigned int, Reader<double, unsigned int>::MaxwellSensor> >,
  _Distance = unsigned int] not viable: expects an l-value for 1st argument
advance(_InputIterator& __i, _Distance __n)

So how can I advance the iterator of a standard map?

I also tried auto maxIt = maxwellSensors.begin() + index; but without luck.

And: I want to avoid a for loop like:

auto maxIt = maxwellSensors.begin();
for(uint i = 0; i < index; ++i){
    ++maxIt;
}

Is there any other possibility? Many thanks in advance!

Laudanum answered 7/2, 2014 at 10:44 Comment(4)
Your question doesn't entirely make sense. What are you trying to get with your std::advance() call? An iterator for the end? An iterator for element "index"? Something else?Santo
You really shouldn't use find_if on a map, but the map::find member. Otherwise you throw away all the benefits of using a map in the first place.Ennoble
Basically you need to read the compiler error message that you got when you tried to use std::advance.Haplosis
Darn! I didn't see the wood for the trees. expects an l-value for 1st argument should've told me everything...Laudanum
P
14

The iterator_category of a std::map is that of a Bidirectional Iterator. This means that there is no O(1) multi-step increment such as you have for Random Access Iterators. For the latter, you can write:

auto it = my_vector.begin(); // std::vector has random access iterators
std::advance(it, 4);         // NOT a loop, will call it += 4;
it += 4;                     // shorthand, not recommended in generic code 

For the former, you have

auto it = my_map.begin();    // std::map has bidirectional iterators
std::advance(it, 4);         // for (auto i = 0; i < 4; ++i) ++it;

Note that std::advance has void return type. If you want to return an advanced iterator, you can use std::next

auto it = my_map.begin();
auto it4 = std::next(it, 4); // copies it, then advances and returns that copy

The step parameter for std::advance can be negative, in which case it will call --it under the covers. If you want to return a decremented iterator, you can use std::prev.

Pernas answered 7/2, 2014 at 11:9 Comment(0)
M
3

The prototype of advance is:

template <class InputIterator, class Distance>
  void advance (InputIterator& it, Distance n);

So you have to do:

auto maxIt = maxwellSensors.begin();
std::advance(maxIt, index);

instead of

auto maxIt = std::advance(maxwellSensors.begin(), index); // Failed
Mien answered 7/2, 2014 at 10:50 Comment(0)
S
2

You need to supply a variable to advance. So, the following will work:

auto it = maxwellSensors.begin();
std::advance(it, index);

However, this internally just does a loop like you wanted to avoid. A loop is the only way to advance a map iterator. If you use a vector or a deque, then continer.begin() + index is valid because they have Random Access iterators,, while map does not.

I think what you actually want is this, which finds index in your map and returns an iterator to it:

auto it = maxwellSensors.find(index);
Santo answered 7/2, 2014 at 10:50 Comment(0)
A
1

See the advance signature and documentation : http://en.cppreference.com/w/cpp/iterator/advance

template< class InputIt, class Distance >
void advance( InputIt& it, Distance n );

It means that the correct usage is :

auto maxIt = maxwellSensors.begin();
std::advance( maxIt , index);
Altheta answered 7/2, 2014 at 10:50 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.