2012-06-21 3 views
8

Possible Duplicate:
remove_if equivalent for std::mapमैं std :: remove_if के साथ std :: set से स्ट्रिंग को क्यों नहीं हटा सकता?</p> <pre><code>set <wstring> strings; // ... </code></pre> <p>मैं एक विधेय के अनुसार तार निकालना चाहते हैं जैसे::

मैं तार का एक सेट है

std::remove_if (strings.begin(), strings.end(), [](const wstring &s) -> bool { return s == L"matching"; }); 

जब मैं इस प्रयास करते हैं, मैं निम्नलिखित संकलक मिल त्रुटि:

c:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\include\algorithm(1840): error C2678: binary '=' : no operator found which takes a left-hand operand of type 'const std::basic_string<_Elem,_Traits,_Ax>' 

ई rror का सुझाव है कि std::string में एक मूल्य-प्रतिलिपि प्रतिलिपि नहीं है (जो अवैध होगा)। क्या std::remove_ifstd::set के साथ उपयोग करना किसी भी तरह से बुरा है? क्या मुझे इसके बजाय कुछ और करना चाहिए जैसे कि set::find() के कई पुनरावृत्तियों के बाद set::erase()?

+0

आपका सरल नमूना 'strings.erase (एल" मिलान ") द्वारा प्रतिस्थापित किया जा सकता है,'। एक मानता है कि आपका * वास्तविक * भविष्यवाणी तुच्छ नहीं है। –

+0

@ रोब हां यह सिर्फ एक उदाहरण था, मुझे फ़ंक्शन ऑब्जेक्ट की आवश्यकता होती है। – Benj

उत्तर

18

std::remove_if (या std::erase) सीमा के सदस्यों के मूल्यों को पुन: असाइन करके काम करता है। यह समझ में नहीं आता कि कैसे std::set डेटा व्यवस्थित करता है, या नोड को अपने आंतरिक पेड़ डेटा संरचना से कैसे हटाया जाता है। दरअसल, set ऑब्जेक्ट के बिना, नोड्स के केवल संदर्भों का उपयोग करके ऐसा करना असंभव है।

मानक एल्गोरिदम को पारदर्शी (या कम से कम लगातार याद रखने योग्य) कम्प्यूटेशनल जटिलताओं के लिए डिज़ाइन किया गया है। पेड़ को पुनर्व्यवस्थित करने की आवश्यकता के कारण set से तत्वों को चुनिंदा रूप से निकालने के लिए एक कार्य ओ (एन लॉग एन) होगा, जो my_set.remove() पर लूप कॉलिंग से बेहतर नहीं है। तो, मानक इसे प्रदान नहीं करता है, और यही आपको लिखने की जरूरत है।

दूसरी तरफ, vector से वस्तुओं को हटाने के लिए एक निष्क्रिय हाथ-कोडित पाश ओ (एन^2) होगा, जबकि std::remove_if ओ (एन) है। इसलिए पुस्तकालय उस मामले में एक वास्तविक लाभ प्रदान करता है।

एक ठेठ लूप (सी ++ 03 शैली):

for (set_t::iterator i = my_set.begin(); i != my_set.end();) { 
    if (condition) { 
     my_set.erase(i ++); // strict C++03 
     // i = my_set.erase(i); // more modern, typically accepted as C++03 
    } else { 
     ++ i; // do not include ++ i inside for () 
    } 
} 

संपादित करें (4 साल बाद!): i ++ वहाँ संदिग्ध लग रहा है। क्या होगा यदि erase पोस्ट-वृद्धि ऑपरेटर इसे अपडेट कर सकने से पहले i को अमान्य कर देता है? यह ठीक है, हालांकि, क्योंकि यह अंतर्निर्मित ऑपरेटर के बजाय operator++ ओवरलोडेड है। फ़ंक्शन सुरक्षित रूप से i इन-प्लेस और अपडेट करता है, तो इसके मूल मान की एक प्रति देता है।

+0

मुझे आश्चर्य है कि उन्होंने "असाइन करने योग्य इटरेटर" (बेहतर अवधि की इच्छा के लिए) की धारणा को सामान्यीकृत क्यों नहीं किया, यह देखते हुए कि इस व्यवहार को प्रदर्शित करने वाले सीरवेल कंटेनर हैं। – Rook

+0

@Rook ऐसी धारणा है, लेकिन यह एक फिक्स नहीं है। समस्या यह है कि 'std :: remove_if' को ओ (एन) के रूप में निर्दिष्ट किया गया है, और एक कार्यान्वयन जो ओ (एन) नहीं है, उसे कानून के पत्र द्वारा' std :: remove_if' नहीं कहा जा सकता है। आप अपने स्वयं के नामस्थान में अपना खुद का कार्यान्वयन प्रदान कर सकते हैं। हालांकि, ओवरलोड एक 'std' में से किसी के साथ संघर्ष करेगा। आप बस एक लूप लिखने से बेहतर हैं। – Potatoswatter

+1

@Rook: इस मामले में समस्या इटेटरेटर नहीं है, लेकिन कंटेनर का 'value_type' है। 'std :: remove_if' * iterator को dereferencing के माध्यम से मानों को संशोधित करता है, लेकिन' std :: set' में 'value_type' एक निरंतर वस्तु है। –

9

त्रुटि संदेश दर्शाता है

no operator found which takes a left-hand operand of type 'const std::basic_string<_Elem,_Traits,_Ax>'

नोट स्थिरांक। संकलक सही है कि std::wstring में operator= नहीं है जिसे किसी कॉन्स्ट ऑब्जेक्ट पर कॉल किया जा सकता है।

स्ट्रिंग कांस्ट क्यों है? जवाब यह है कि std::set में मान अपरिवर्तनीय हैं, क्योंकि किसी सेट में मानों का आदेश दिया जाता है, और एक मान बदलने से सेट में अपना ऑर्डरिंग बदल सकती है, सेट को अमान्य कर दिया जा सकता है।

संकलक सेट के मूल्य की प्रतिलिपि बनाने का प्रयास क्यों कर रहा है?

std::remove_if (और std::remove) वास्तव में कुछ भी मिटा नहीं है (न ही वे कर सकते हैं, क्योंकि उनके पास कंटेनर नहीं है, केवल इटरेटर हैं)। वे क्या करते हैं, रेंज की शुरुआत में मानदंड से मेल नहीं खाते हैं, और मिलान तत्वों के बाद अगले तत्व में एक पुनरावर्तक को वापस करें। तब आपको रेंज के अंत तक लौटाए गए इटरेटर से मैन्युअल रूप से मिटाना होगा। चूंकि एक सेट अपने तत्वों को क्रम में रखता है, इसलिए किसी भी तत्व को चारों ओर स्थानांतरित करना गलत होगा, इसलिए remove_if किसी सेट (या किसी अन्य सहयोगी कंटेनर) पर उपयोग नहीं किया जा सकता है।

संक्षेप में, आप तो जैसे, std::find_if और set::erase के एक पाश का उपयोग करने के लिए क्या है:

template<class V, class P> 
void erase_if(std::set<V>& s, P p) 
{ 
    std::set<V>::iterator e = s.begin(); 
    for (;;) 
    { 
    e = std::find_if(e, s.end(), p); 
    if (e == s.end()) 
     break; 
    e = s.erase(e); 
    } 
} 
+0

यह वास्तव में एक पुस्तकालय के लिए 'std :: remove_if'' std :: set' के साथ संगत, विशेष ज्ञान का उपयोग करके रूट नोड वास्तव में कंटेनर ऑब्जेक्ट के अंदर रहता है। हालांकि, यह ओ (एन) रनटाइम आवश्यकता को पूरा नहीं करेगा। – Potatoswatter

+0

ओह, 'अंत() 'नोड, रूट नहीं, लेकिन आपको विचार मिलता है। – Potatoswatter

+1

+1, आपने तर्क को बहुत अच्छी तरह से लिखा है और एक अच्छा विकल्प प्रदान किया है, लेकिन शायद मैं उस कार्य को 'erase_if' नाम दूंगा। –

संबंधित मुद्दे