2012-03-01 22 views
12

तो, मुझे std :: map, lambda और stl algorithm (remove_if) के साथ समस्या है। असल में, std :: list या std :: vector के साथ एक ही कोड अच्छी तरह से काम करता है।मानचित्र, लैम्ब्डा, remove_if

मेरे परीक्षण उदाहरण:

#include <map> 
#include <iostream> 
#include <algorithm> 

struct Foo 
{ 
    Foo() : _id(0) {} 
    Foo(int id) : _id(id) 
    { 

    } 

    int _id;  
}; 
typedef std::map<int, Foo> FooMap; 


int main() 
{ 
    FooMap m; 
    for (int i = 0; i < 10; ++i) 
     m[i + 100] = Foo(i); 

    int removeId = 6; 
    // <<< Error here >>> 
    std::remove_if(m.begin(), m.end(), [=](const FooMap::value_type & item) { return item.second._id == removeId ;}); 

    for (auto & item : m) 
     std::cout << item.first << " = " << item.second._id << "\n";  

    return 0; 
} 

त्रुटि संदेश:

In file included from /usr/include/c++/4.6/utility:71:0, 
       from /usr/include/c++/4.6/algorithm:61, 
       from main.cxx:1: 
/usr/include/c++/4.6/bits/stl_pair.h: In member function ‘std::pair<_T1, _T2>& std::pair<_T1, _T2>::operator=(std::pair<_T1, _T2>&&) [with _T1 = const int, _T2 = Foo, std::pair<_T1, _T2> = std::pair<const int, Foo>]’: 
/usr/include/c++/4.6/bits/stl_algo.h:1149:13: instantiated from ‘_FIter std::remove_if(_FIter, _FIter, _Predicate) [with _FIter = std::_Rb_tree_iterator<std::pair<const int, Foo> >, _Predicate = main()::<lambda(const value_type&)>]’ 
main.cxx:33:114: instantiated from here 
/usr/include/c++/4.6/bits/stl_pair.h:156:2: error: assignment of read-only member ‘std::pair<const int, Foo>::first’ 

मुझे समझ नहीं आता यहाँ क्या गलत है। तो, मुझे खुशी है कि इसके बारे में कुछ सलाह/दिशानिर्देश पढ़ने के लिए। मेरा लक्ष्य - std :: map और एल्गोरिदम, जैसे remove_if के साथ नई लैम्ब्डा-स्टाइल का उपयोग करें।

जी ++ 4.6, -स्टडी = सी ++ 0 एक्स।

+2

'remove_if' iterators की एक जोड़ी स्वीकार करता है, और एक पुनरावर्तक देता है। आपको लगता है कि यह ** ** से तत्वों को हटा देता है? –

उत्तर

27

समस्या यह है कि std::map<K,V>::value_typestd::pair<const K, V> है, उर्फ ​​.firstconst है और असाइन करने योग्य नहीं है। Lambdas के पास समस्या से कोई लेना देना नहीं है।

std::remove_if कंटेनर के तत्वों को चारों ओर ले जाकर वस्तुओं को "हटा देता है", ताकि सब कुछ जो भविष्य में फिट न हो, वापस लौटाए जाने वाले से पहले सामने हो। उसके बाद सब कुछ इटेटरेटर अनिर्दिष्ट है। यह सरल असाइनमेंट के साथ करता है, और चूंकि आप const वैरिएबल को असाइन नहीं कर सकते हैं, तो आपको वह त्रुटि मिलती है।

नाम remove थोड़ा भ्रामक हो सकता है और इस मामले में, तुम सच में erase_if चाहते हैं, लेकिन अफसोस, जो मौजूद नहीं है। आप सभी से अधिक आइटम पुनरावृत्ति और map.erase(iterator) के साथ हाथ से उन्हें मिटा के साथ करना होगा:

for(auto it = map.begin(), ite = map.end(); it != ite;) 
{ 
    if(it->second._id == remove_id) 
    it = map.erase(it); 
    else 
    ++it; 
} 

क्योंकि आप अन्य iterators अवैध बिना पेड़ में अलग-अलग नोड्स को मिटा सकते हैं यह सुरक्षित है। ध्यान दें कि मैंने लूप हेडर में ही इटरेटर को बढ़ाया नहीं है, क्योंकि इससे उस स्थिति में कोई तत्व छोड़ा जाएगा जहां आप नोड मिटाते हैं।


† अब तक, आपने गौर किया जाना चाहिए कि इस std::map के आदेश देने में कहर बरपाने ​​होता है, जो कारण है कि कुंजी const है - तो आप एक के बाद किसी भी तरह से आदेश प्रभावित नहीं कर सकते आइटम डाला गया है।

+0

उत्तर के लिए धन्यवाद। तो, बदसूरत कोड के बिना std :: मानचित्र से आइटम को हटाने का कोई शानदार तरीका है: 'शून्य हटाएंप्रैममैप (FooMap और m, int id) { \t (ऑटो it = m.begin(), end = m। अंत();! यह = अंत, यह ++) \t \t { \t \t अगर (है-> second._id == आईडी) \t \t \t { \t \t \t मीटर।इसे मिटाएं); \t \t \t ब्रेक; \t \t} \t} } ' – Reddy

+0

@ रेड्डी: नहीं, मुझे कोई अन्य तरीका नहीं है। बीटीडब्ल्यू, यदि आपकी आईडी गैर-अद्वितीय हैं, तो आप मानचित्र में पहले तत्व को मिटा देंगे। यदि वे हैं, तो वह लूप ठीक है। – Xeo

+0

हाँ, मुझे इसके बारे में पता है। – Reddy

3

आप मानचित्र के लिए ढूंढ और मिटा सकते हैं। यह remove_if के रूप में सुविधाजनक नहीं है, लेकिन यह आपके लिए सबसे अच्छा हो सकता है।

int removeId = 6; 
auto foundIter = m.find(removeId); 

// if removeId is not found you will get an error when you try to erase m.end() 
if(foundIter != m.end()) 
{ 
    m.erase(foundIter); 
}