Làm cách nào để xóa bản đồ trong khi lặp lại? giống:
std::map<K, V> map;
for(auto i : map)
if(needs_removing(i))
// remove it from the map
Nếu tôi sử dụng map.erase
nó sẽ làm mất hiệu lực các trình vòng lặp
Làm cách nào để xóa bản đồ trong khi lặp lại? giống:
std::map<K, V> map;
for(auto i : map)
if(needs_removing(i))
// remove it from the map
Nếu tôi sử dụng map.erase
nó sẽ làm mất hiệu lực các trình vòng lặp
Câu trả lời:
Các thành ngữ kết hợp-container xóa tiêu chuẩn:
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;
}
}
Lưu ý rằng chúng tôi thực sự muốn một for
vòng lặp thông thường ở đây, vì chúng tôi đang sửa đổi chính container. Vòng lặp dựa trên phạm vi nên được dành riêng cho các tình huống mà chúng ta chỉ quan tâm đến các yếu tố. Cú pháp cho RBFL làm cho điều này rõ ràng bằng cách thậm chí không để lộ container bên trong thân vòng lặp.
Biên tập. Pre-C ++ 11, bạn không thể xóa const-iterators. Ở đó bạn sẽ phải nói:
for (std::map<K,V>::iterator it = m.begin(); it != m.end(); ) { /* ... */ }
Xóa một phần tử khỏi một thùng chứa không phải là mâu thuẫn với hằng số của phần tử. Bằng cách tương tự, nó luôn luôn là hoàn toàn hợp pháp đối với delete p
nơi p
là một con trỏ đến hằng số. Sự kiên định không ràng buộc suốt đời; giá trị const trong C ++ vẫn có thể dừng tồn tại.
for (int i = 0; i < v.size(); i++)
. Ở đây chúng ta phải nói v[i]
bên trong vòng lặp, tức là chúng ta phải đề cập rõ ràng đến container. Mặt khác, RBFL giới thiệu biến vòng lặp có thể sử dụng trực tiếp làm giá trị và do đó, không có kiến thức nào về container được yêu cầu bên trong vòng lặp. Đây là manh mối cho việc sử dụng RBFL dự định cho các vòng lặp mà không phải biết về container. Xóa là hoàn toàn ngược lại, trong đó tất cả là về container.
it
để có được trình lặp tiếp theo, hợp lệ, và sau đó xóa cái cũ. Nó không hoạt động theo cách khác!
it = v.erase(it);
bây giờ cũng hoạt động cho các bản đồ. Đó là, xóa () trên tất cả các yếu tố kết hợp bây giờ trả về trình lặp tiếp theo. Vì vậy, loại bùn cũ yêu cầu tăng sau ++ trong phần xóa (), không còn cần thiết nữa. Điều này (nếu đúng) là một điều tốt, vì bùn được dựa vào ma thuật ghi đè-tăng-sau-trong-chức năng-cuộc gọi, được "cố định" bởi những người duy trì newbie để tăng số lần gọi ra khỏi chức năng hoặc để hoán đổi nó đến một tiền đề "bởi vì đó chỉ là một thứ phong cách", v.v.
it++
trong if
và else
khối? Sẽ không đủ để gọi nó một lần sau những điều này?
Cá nhân tôi thích mẫu này rõ ràng hơn và đơn giản hơn, với chi phí của một biến phụ:
for (auto it = m.cbegin(), next_it = it; it != m.cend(); it = next_it)
{
++next_it;
if (must_delete)
{
m.erase(it);
}
}
Ưu điểm của phương pháp này:
it
và next_it
vẫn được cố định trong suốt quá trình lặp, cho phép bạn dễ dàng thêm các câu lệnh bổ sung liên quan đến chúng mà không cần che đầu xem chúng có hoạt động như dự định không (trừ khi bạn không thể sử dụng it
sau khi xóa nó) .Tóm lại "Làm cách nào để tôi xóa khỏi bản đồ trong khi lặp lại nó?"
Từ bản đồ GCC impl (lưu ý 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
Ví dụ với phong cách cũ và mới:
#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;
}
in:
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++);
gì?
if(mi.empty()) break;
.
Bản nháp C ++ 20 chứa chức năng tiện lợi std::erase_if
.
Vì vậy, bạn có thể sử dụng chức năng đó để làm điều đó như một lớp lót.
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);});
Khá buồn hả? Cách tôi thường làm là xây dựng một thùng chứa các vòng lặp thay vì xóa trong quá trình truyền tải. Sau đó lặp qua container và sử dụng 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)
}
Giả sử C ++ 11, đây là phần thân vòng lặp một lớp, nếu điều này phù hợp với phong cách lập trình của bạn:
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);
Một vài thay đổi phong cách nhỏ khác:
Map::const_iterator
) khi có thể / thuận tiện, qua sử dụng auto
.using
cho các loại mẫu, để làm cho các loại phụ trợ ( Map::const_iterator
) dễ đọc / bảo trì hơn.