2016-07-25 1 views
0

J'utilise plusieurs std::map conteneurs de données de type qui sont les suivantes:Erreur de segmentation lors de l'itération et de l'effacement de certains éléments de std :: map?

std::map<int, std::vector<cv::Point> > mapGoodContours; 
    std::map<int, EllipseProperties> ellipsePropertiesMap; 
    std::map<int, float> m_markerRadiusMap; 
    std::map<int,cv::Point2f> markers;//This is part of another class 

J'itérer ces conteneurs et d'effacer certains éléments après ces éléments répondent à certaines conditions, comme indiqué dans le code suivant.

auto radiusMapCounter = m_markerRadiusMap.begin(); 
    auto markerCounter = frames.markers.begin(); 
    auto goodContoursCounter = mapGoodContours.begin(); 

    if(m_markerRadiusMap.size()==ellipsePropertiesMap.size() && frames.markers.size()==ellipsePropertiesMap.size() 
      && mapGoodContours.size()==ellipsePropertiesMap.size()) 
    { 
     for(auto ellipsePropertyCounter = ellipsePropertiesMap.begin(); ellipsePropertyCounter != ellipsePropertiesMap.end(); ellipsePropertyCounter++) 
     { 

      float upperLimit = (float)m_upperFactor*(float)ellipsePropertyCounter->second.radius; 
      float lowerLimit = (float)m_lowerFactor*(float)ellipsePropertyCounter->second.radius; 
      if(ellipsePropertyCounter->second.minDistanceFromOtherEllipse>upperLimit 
         || ellipsePropertyCounter->second.minDistanceFromOtherEllipse<lowerLimit) 
      { 
       ellipsePropertiesMap.erase(ellipsePropertyCounter); 
       m_markerRadiusMap.erase(radiusMapCounter); 
       frames.markers.erase(markerCounter); 
       mapGoodContours.erase(goodContoursCounter); 
      } 
      else 
      { 
       smallContours.push_back(goodContoursCounter->second); 
      } 

      radiusMapCounter++; 
      markerCounter++; 
      goodContoursCounter++; 
     } 
    } 

Je suis déconcerté de constater que j'ai parfois des défauts de segmentation comme indiqué dans l'image. enter image description here La faute pointe spécifiquement vers la ligne avec le code radiusMapCounter++;

Qu'est-ce que je fais mal?

+2

Copie possible de [règles d'invalidation d'Iterator] (http://stackoverflow.com/questions/6438086/iterator-invalidation-rules) – MikeCAT

Répondre

3

Vous ne pouvez pas incrémenter l'itérateur après avoir effacé l'élément à partir duquel il est dirigé. Créer une copie de l'itérateur, l'incrémenter, supprimer l'élément via la copie.

Si vous utilisez C++ 11 ou une version ultérieure, std::map::erase(...) renvoie l'itérateur après le dernier élément supprimé, vous pouvez donc l'utiliser au lieu d'incrémenter celui qui est invalide. Pas besoin de créer une copie d'itérateur à utiliser dans erase dans ce cas.

for(auto ellipsePropertyCounter = ellipsePropertiesMap.begin(); 
    ellipsePropertyCounter != ellipsePropertiesMap.end(); 
    /* do not increment iterator here */) 
{ 

    float upperLimit = (float)m_upperFactor*(float)ellipsePropertyCounter->second.radius; 
    float lowerLimit = (float)m_lowerFactor*(float)ellipsePropertyCounter->second.radius; 
    if(ellipsePropertyCounter->second.minDistanceFromOtherEllipse>upperLimit 
       || ellipsePropertyCounter->second.minDistanceFromOtherEllipse<lowerLimit) 
    { 
     ellipsePropertyCounter = ellipsePropertiesMap.erase(ellipsePropertyCounter); 
     radiusMapCounter = m_markerRadiusMap.erase(radiusMapCounter); 
     markerCounter = frames.markers.erase(markerCounter); 
     goodContoursCounter = mapGoodContours.erase(goodContoursCounter); 
    } 
    else 
    { 
     smallContours.push_back(goodContoursCounter->second); 

     radiusMapCounter++; 
     markerCounter++; 
     goodContoursCounter++; 
     ellipsePropertyCounter++; // increment loop iterator only in 'else' case 
    } 
} 
0

Voici une solution C++ 17.

Première indexer, qui est C++ 14 et vous permet de créer et de déballer les paquets d'index en ligne sans aides ad hoc au point d'utilisation:

template<class=void,std::size_t...Is> 
void indexer(std::index_sequence<Is>...) { 
    return [](auto&& f) { 
    using discard=int[]; 
    (void)discard{0,((
     f(std::integral_constant<std::size_t, Is>{}) 
    ),void(),0)...}; 
    }; 
} 
template<std::size_t N> 
void indexer() { 
    return indexer(std::make_index_sequence<N>{}); 
} 

Maintenant, erase_if, qui devrait être SFINAE limitée à seulement le travail sur associatif (et peut-être d'autres à base de noeud) conteneurs:

template<class Test, class...Maps> 
bool erase_if(Test&& test, Maps&&...maps) { 
    using std::begin; using std::end; 
    std::tuple< decltype(begin(maps))... > its; 
    std::tuple< decltype(begin(maps))... > ends; 
    auto m_tup = std::tie(maps...); 
    auto index = indexer<sizeof...(maps)>; 
    index(
    [&](auto i){ 
     std::get<i>(its) = begin(std::get<i>(m_tup)); 
     std::get<i>(ends) = end(std::get<i>(m_tup)); 
    } 
); 
    while (its != ends) { 
    auto deref_then_test = [&test](auto...its) { 
     return test((*its)...); 
    }; 
    if (std::apply(deref_then_test,its)) { 
     index(
     [&](auto i){ 
      std::get<i>(its) = std::get<i>(m_tup).erase(std::get<i>(its)); 
     } 
    ); 
    } else { 
     index(
     [&](auto i){++std::get<i>(its);} 
    ); 
    } 
    } 
} 

cela permet de vous effacer plusieurs conteneurs tests basés sur de l'un d'eux.

Code non testé, probablement des fautes de frappe.