ฉันจะลบออกจากแผนที่ในขณะที่วนซ้ำได้อย่างไร ชอบ:
std::map<K, V> map;
for(auto i : map)
if(needs_removing(i))
// remove it from the map
ถ้าฉันใช้map.erase
มันจะทำให้ตัววนซ้ำใช้ไม่ได้
ฉันจะลบออกจากแผนที่ในขณะที่วนซ้ำได้อย่างไร ชอบ:
std::map<K, V> map;
for(auto i : map)
if(needs_removing(i))
// remove it from the map
ถ้าฉันใช้map.erase
มันจะทำให้ตัววนซ้ำใช้ไม่ได้
คำตอบ:
การเชื่อมโยงคอนเทนเนอร์มาตรฐานลบสำนวน:
for (auto it = m.cbegin(); it != m.cend() /* not hoisted */; /* no increment */)
{
if (must_delete)
{
m.erase(it++); // or "it = m.erase(it)" since C++11
}
else
{
++it;
}
}
โปรดทราบว่าเราต้องการfor
ลูปสามัญที่นี่เนื่องจากเรากำลังแก้ไขคอนเทนเนอร์เอง การวนซ้ำตามช่วงควรถูกสงวนไว้อย่างเคร่งครัดสำหรับสถานการณ์ที่เราสนใจเฉพาะองค์ประกอบ ไวยากรณ์สำหรับ RBFL ทำให้สิ่งนี้ชัดเจนโดยไม่เปิดเผยคอนเทนเนอร์ภายในเนื้อความลูป
แก้ไข Pre-C ++ 11 คุณไม่สามารถลบผู้ทำซ้ำ ที่นั่นคุณจะต้องพูดว่า:
for (std::map<K,V>::iterator it = m.begin(); it != m.end(); ) { /* ... */ }
การลบองค์ประกอบออกจากคอนเทนเนอร์ไม่ได้ขัดแย้งกับความคงตัวขององค์ประกอบ โดยการเปรียบเทียบมันถูกต้องตามกฎหมายอย่างสมบูรณ์แบบเสมอกับdelete p
ที่p
เป็นตัวชี้ไปยังคงที่ Constness ไม่ จำกัด อายุการใช้งาน ค่า const ใน C ++ ยังคงสามารถหยุดอยู่ได้
for (int i = 0; i < v.size(); i++)
ดีคมชัดนี้การก่อสร้างในศตวรรษที่ ที่นี่เราต้องพูดv[i]
ในวงคือเราจะต้องพูดถึงภาชนะ RBFL ในอีกทางหนึ่งแนะนำตัวแปรลูปที่ใช้งานได้โดยตรงเป็นค่าและไม่จำเป็นต้องมีความรู้เกี่ยวกับคอนเทนเนอร์ภายในลูป นี่เป็นเงื่อนงำการใช้ RBFL สำหรับลูปที่ไม่จำเป็นต้องรู้เกี่ยวกับคอนเทนเนอร์ การลบเป็นสถานการณ์ตรงกันข้ามที่สมบูรณ์ซึ่งทุกอย่างเกี่ยวกับคอนเทนเนอร์
it
จะได้รับ iterator ต่อไปถูกต้องแล้วลบเก่า มันไม่ทำงานในทางกลับกัน!
it = v.erase(it);
ตอนนี้ใช้ได้กับแผนที่ด้วยนั่นคือลบ () ในองค์ประกอบที่เชื่อมโยงทั้งหมดตอนนี้ส่งคืนตัววนซ้ำถัดไป ดังนั้นจึงไม่จำเป็นต้องใช้ kludge แบบเก่าที่เพิ่มโพสต์เพิ่มขึ้นภายในการลบ () สิ่งนี้ (ถ้าเป็นจริง) เป็นสิ่งที่ดีเนื่องจาก kludge อาศัยเวทมนตร์เวรที่โพสต์เพิ่มขึ้นภายในฟังก์ชั่นการโทร "คงที่" โดยผู้ดูแลรักษามือใหม่ที่จะเพิ่มขึ้นจากการเรียกใช้ฟังก์ชันหรือเพื่อสลับ ไป preincrement "เพราะนั่นเป็นเพียงสิ่งสไตล์" ฯลฯ
it++
เข้าif
และ else
บล็อก? มันจะไม่เพียงพอที่จะเรียกมันอีกครั้งหลังจากนี้?
โดยส่วนตัวแล้วฉันชอบรูปแบบนี้ที่ชัดเจนและเรียบง่ายขึ้นเล็กน้อยโดยมีค่าใช้จ่ายของตัวแปรพิเศษ:
for (auto it = m.cbegin(), next_it = it; it != m.cend(); it = next_it)
{
++next_it;
if (must_delete)
{
m.erase(it);
}
}
ข้อดีของวิธีนี้:
it
และnext_it
ยังคงอยู่ตลอดการวนซ้ำทำให้คุณสามารถเพิ่มคำสั่งเพิ่มเติมที่อ้างถึงได้อย่างง่ายดายโดยไม่ต้อง headcratching ว่าจะทำงานได้ตามที่ต้องการหรือไม่ยกเว้นว่าคุณไม่สามารถใช้งานได้it
หลังจากลบทิ้ง) .โดยย่อ "ฉันจะลบออกจากแผนที่ขณะทำซ้ำได้อย่างไร"
จากแผนที่ GCC impl (หมายเหตุGXX_EXPERIMENTAL_CXX0X ):
#ifdef __GXX_EXPERIMENTAL_CXX0X__
// _GLIBCXX_RESOLVE_LIB_DEFECTS
// DR 130. Associative erase should return an iterator.
/**
* @brief Erases an element from a %map.
* @param position An iterator pointing to the element to be erased.
* @return An iterator pointing to the element immediately following
* @a position prior to the element being erased. If no such
* element exists, end() is returned.
*
* This function erases an element, pointed to by the given
* iterator, from a %map. Note that this function only erases
* the element, and that if the element is itself a pointer,
* the pointed-to memory is not touched in any way. Managing
* the pointer is the user's responsibility.
*/
iterator
erase(iterator __position)
{ return _M_t.erase(__position); }
#else
/**
* @brief Erases an element from a %map.
* @param position An iterator pointing to the element to be erased.
*
* This function erases an element, pointed to by the given
* iterator, from a %map. Note that this function only erases
* the element, and that if the element is itself a pointer,
* the pointed-to memory is not touched in any way. Managing
* the pointer is the user's responsibility.
*/
void
erase(iterator __position)
{ _M_t.erase(__position); }
#endif
ตัวอย่างที่มีสไตล์เก่าและใหม่:
#include <iostream>
#include <map>
#include <vector>
#include <algorithm>
using namespace std;
typedef map<int, int> t_myMap;
typedef vector<t_myMap::key_type> t_myVec;
int main() {
cout << "main() ENTRY" << endl;
t_myMap mi;
mi.insert(t_myMap::value_type(1,1));
mi.insert(t_myMap::value_type(2,1));
mi.insert(t_myMap::value_type(3,1));
mi.insert(t_myMap::value_type(4,1));
mi.insert(t_myMap::value_type(5,1));
mi.insert(t_myMap::value_type(6,1));
cout << "Init" << endl;
for(t_myMap::const_iterator i = mi.begin(); i != mi.end(); i++)
cout << '\t' << i->first << '-' << i->second << endl;
t_myVec markedForDeath;
for (t_myMap::const_iterator it = mi.begin(); it != mi.end() ; it++)
if (it->first > 2 && it->first < 5)
markedForDeath.push_back(it->first);
for(size_t i = 0; i < markedForDeath.size(); i++)
// old erase, returns void...
mi.erase(markedForDeath[i]);
cout << "after old style erase of 3 & 4.." << endl;
for(t_myMap::const_iterator i = mi.begin(); i != mi.end(); i++)
cout << '\t' << i->first << '-' << i->second << endl;
for (auto it = mi.begin(); it != mi.end(); ) {
if (it->first == 5)
// new erase() that returns iter..
it = mi.erase(it);
else
++it;
}
cout << "after new style erase of 5" << endl;
// new cend/cbegin and lambda..
for_each(mi.cbegin(), mi.cend(), [](t_myMap::const_reference it){cout << '\t' << it.first << '-' << it.second << endl;});
return 0;
}
พิมพ์:
main() ENTRY
Init
1-1
2-1
3-1
4-1
5-1
6-1
after old style erase of 3 & 4..
1-1
2-1
5-1
6-1
after new style erase of 5
1-1
2-1
6-1
Process returned 0 (0x0) execution time : 0.021 s
Press any key to continue.
mi.erase(it++);
อะไร?
if(mi.empty()) break;
นี้หลังจากการลบดีกว่าที่จะแทรก
ร่าง C ++ 20 มีฟังก์ชันอำนวยความสะดวก std::erase_if
ร่างมีฟังก์ชั่นอำนวยความสะดวก
ดังนั้นคุณสามารถใช้ฟังก์ชั่นนั้นเป็นซับเดียวได้
std::map<K, V> map_obj;
//calls needs_removing for each element and erases it, if true was reuturned
std::erase_if(map_obj,needs_removing);
//if you need to pass only part of the key/value pair
std::erase_if(map_obj,[](auto& kv){return needs_removing(kv.first);});
เศร้ามากใช่มั้ย วิธีที่ฉันมักจะทำคือสร้างคอนเทนเนอร์ของตัววนซ้ำแทนการลบระหว่างการสำรวจเส้นทาง จากนั้นวนซ้ำผ่านคอนเทนเนอร์และใช้ map.erase ()
std::map<K,V> map;
std::list< std::map<K,V>::iterator > iteratorList;
for(auto i : map ){
if ( needs_removing(i)){
iteratorList.push_back(i);
}
}
for(auto i : iteratorList){
map.erase(*i)
}
สมมติว่า C ++ 11 ต่อไปนี้เป็นตัววนลูปแบบหนึ่งซับถ้าสิ่งนี้สอดคล้องกับสไตล์การเขียนโปรแกรมของคุณ:
using Map = std::map<K,V>;
Map map;
// Erase members that satisfy needs_removing(itr)
for (Map::const_iterator itr = map.cbegin() ; itr != map.cend() ; )
itr = needs_removing(itr) ? map.erase(itr) : std::next(itr);
การเปลี่ยนแปลงสไตล์รองอื่น ๆ สองสามรายการ:
Map::const_iterator
) เมื่อเป็นไปได้ / สะดวกใช้มากกว่าauto
สะดวกมากกว่าการใช้using
สำหรับประเภทเทมเพลตเพื่อทำให้ประเภทเสริม ( Map::const_iterator
) อ่าน / ดูแลง่ายขึ้น