จะเกิดอะไรขึ้นถ้าคุณเรียกลบ () บนองค์ประกอบแผนที่ในขณะที่ทำซ้ำตั้งแต่ต้นจนจบ


134

ในรหัสต่อไปนี้ฉันวนลูปผ่านแผนที่และทดสอบว่าจำเป็นต้องลบองค์ประกอบหรือไม่ ปลอดภัยหรือไม่ที่จะลบองค์ประกอบและทำซ้ำต่อไปหรือฉันต้องรวบรวมคีย์ในคอนเทนเนอร์อื่นและทำลูปที่สองเพื่อเรียกการลบ ()

map<string, SerialdMsg::SerialFunction_t>::iterator pm_it;
for (pm_it = port_map.begin(); pm_it != port_map.end(); pm_it++)
{
    if (pm_it->second == delete_this_id) {
        port_map.erase(pm_it->first);
    }
}

UPDATE: แน่นอนจากนั้นฉันก็อ่านคำถามนี้ซึ่งฉันไม่คิดว่าจะเกี่ยวข้อง แต่ตอบคำถามของฉัน


โปรดทราบในคำถามที่ใช้std::remove_ifไม่ได้กับstd:map
socketpair

คำตอบ:


185

C ++ 11

สิ่งนี้ได้รับการแก้ไขแล้วใน C ++ 11 (หรือการลบได้รับการปรับปรุง / ทำให้สอดคล้องกันในทุกประเภทคอนเทนเนอร์)
วิธีการลบจะส่งกลับตัวทำซ้ำถัดไป

auto pm_it = port_map.begin();
while(pm_it != port_map.end())
{
    if (pm_it->second == delete_this_id)
    {
        pm_it = port_map.erase(pm_it);
    }
    else
    {
        ++pm_it;
    }
}

C ++ 03

การลบองค์ประกอบในแผนที่ไม่ได้ทำให้ตัวทำซ้ำใด ๆ เป็นโมฆะ
(นอกเหนือจากตัววนซ้ำในองค์ประกอบที่ถูกลบ)

การแทรกหรือลบจริง ๆ ไม่ได้ทำให้ตัวทำซ้ำใด ๆ เป็นโมฆะ:

ดูคำตอบนี้ด้วย:
Mark Ransom Technique

แต่คุณจำเป็นต้องอัปเดตรหัสของคุณ:
ในรหัสของคุณคุณจะเพิ่ม pm_it หลังจากลบการโทร ในตอนนี้มันสายเกินไปและไม่ถูกต้องแล้ว

map<string, SerialdMsg::SerialFunction_t>::iterator pm_it = port_map.begin();
while(pm_it != port_map.end())
{
    if (pm_it->second == delete_this_id)
    {
        port_map.erase(pm_it++);  // Use iterator.
                                  // Note the post increment.
                                  // Increments the iterator but returns the
                                  // original value for use by erase 
    }
    else
    {
        ++pm_it;           // Can use pre-increment in this case
                           // To make sure you have the efficient version
    }
}

ลำดับของการประเมินการเพิ่มขึ้นของนิพจน์ postfix pm_it++รับประกันว่าจะดำเนินการก่อนที่จะป้อนฟังก์ชันหรือไม่
David Rodríguez - dribeas

4
@David Rodríguez - dribeas: ใช่ มาตรฐานรับประกันว่านิพจน์อาร์กิวเมนต์ทั้งหมดจะได้รับการประเมินอย่างสมบูรณ์ก่อนที่ฟังก์ชันจะถูกเรียกใช้ เป็นผลลัพธ์ของการเพิ่มโพสต์ที่ส่งผ่านไปยังฟังก์ชันลบ () ใช่แล้วการเพิ่มโพสต์ของ pm_it จะเสร็จสิ้นก่อนที่จะเรียกลบ ()
Martin York

หมายเหตุ: บรรทัดสำหรับบรรทัดเกือบตรงกับตัวอย่างที่เชื่อมโยงกันใน "Effective STL" ของ Scott Meyer รายการที่ 9
Ogre Psalm33

สำหรับ (auto pm_t = port_map.begin (); pm_it! = port_map.end ();) {... }
Andrey Syrokomskiy

4
@iboisver: บนเวกเตอร์ การใช้ลบ () ทำให้ตัววนซ้ำทั้งหมดของอาร์เรย์ไม่ถูกต้องหลังจากจุดลบ (ไม่ใช่แค่จุดสิ้นสุด) นี่คือคุณสมบัติของSequenceคอนเทนเนอร์ คุณสมบัติพิเศษของAssociativeคอนเทนเนอร์คือตัวทำซ้ำจะไม่ถูกยกเลิกโดยการลบหรือแทรก (เว้นแต่จะชี้ไปที่องค์ประกอบที่ถูกลบ) เวกเตอร์และลบ usign iterators มีรายละเอียดอยู่ในคำถามที่เหมาะสมstackoverflow.com/a/3938847/14065
Martin York

12

นี่คือวิธีที่ฉันทำ ...

typedef map<string, string>   StringsMap;
typedef StringsMap::iterator  StrinsMapIterator;

StringsMap m_TheMap; // Your map, fill it up with data    

bool IsTheOneToDelete(string str)
{
     return true; // Add your deletion criteria logic here
}

void SelectiveDelete()
{
     StringsMapIter itBegin = m_TheMap.begin();
     StringsMapIter itEnd   = m_TheMap.end();
     StringsMapIter itTemp;

     while (itBegin != itEnd)
     {
          if (IsTheOneToDelete(itBegin->second)) // Criteria checking here
          {
               itTemp = itBegin;          // Keep a reference to the iter
               ++itBegin;                 // Advance in the map
               m_TheMap.erase(itTemp);    // Erase it !!!
          }
          else
               ++itBegin;                 // Just move on ...
     }
}

หากคุณลบจุดสิ้นสุดของเวกเตอร์ด้วย (itEnd) การตรวจสอบสุดท้าย (เงื่อนไข while) จะตรงกับตัววนซ้ำที่ไม่ถูกต้อง (itEnd) ไม่ดี.
Agostino

1

นี่คือวิธีที่ฉันจะทำโดยประมาณ:

bool is_remove( pair<string, SerialdMsg::SerialFunction_t> val )
{
    return val.second == delete_this_id;
}

map<string, SerialdMsg::SerialFunction_t>::iterator new_end = 
    remove_if (port_map.begin( ), port_map.end( ), is_remove );

port_map.erase (new_end, port_map.end( ) );

มีบางอย่างที่แปลกเกี่ยวกับ

val.second == delete_this_id

แต่ฉันคัดลอกมาจากโค้ดตัวอย่างของคุณ

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.