Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

advance the iterator of a standard map

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!

like image 487
Rico-E Avatar asked Feb 07 '14 10:02

Rico-E


2 Answers

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.

like image 166
TemplateRex Avatar answered Sep 24 '22 02:09

TemplateRex


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
like image 20
Jarod42 Avatar answered Sep 27 '22 02:09

Jarod42