Можно ли удалить элементы из std:: list, когда я повторяю его? Например, так:
std::list<int> lst;
//....
for (std::list<int> itr = lst.begin(); itr != lst.end(); itr++)
{
if (*itr > 10)
lst.remove(*itr);
}
? И почему?
Можно ли удалить элементы из std:: list, когда я повторяю его? Например, так:
std::list<int> lst;
//....
for (std::list<int> itr = lst.begin(); itr != lst.end(); itr++)
{
if (*itr > 10)
lst.remove(*itr);
}
? И почему?
Правильный код выглядит следующим образом:
for (std::list<int>::iterator itr = lst.begin(); itr != lst.end(); /*nothing*/)
{
if (*itr > 10)
itr = lst.erase(itr);
else
++itr;
}
Когда вы удаляете элемент из списка, вы можете аннулировать итератор (если он указывает на удаляемый элемент). Поэтому вам нужно удалить с помощью erase
(который возвращает действительный итератор, указывающий на следующий элемент).
Еще лучшая идея будет использовать std::remove_if
:
bool greater_than_10(int x)
{
return x > 10;
}
lst.remove_if(greater_than_10);
Если ваш компилятор поддерживает lambdas, вы можете поставить его еще короче:
lst.remove_if([](int x){ return x > 10; });
(я не тестировал этот код, поскольку мой компилятор не так уж и нов, лямбда-функция, к счастью, украдена из ответа @John Dibling.)
Фактически, удаление из списка отменяет только итераторы, указывающие на удаляемый элемент. Остерегайтесь, однако, что другие контейнеры STL не имеют этого свойства.
Итак, короче говоря: вообще говоря, вы не должны удалять элементы из списка во время итерации через него, потому что удаление может привести к аннулированию итератора (и программа, возможно, произойдет сбой). Если вы, тем не менее, уверены, что элементы, которые вы удаляете, не являются значениями, на которые ссылается любой из итераторов, которые вы используете в момент удаления, вы можете удалить.
Остерегайтесь того, что для других контейнеров STL (например, векторов) ограничение еще более строгое: удаление из контейнера делает недействительными не только итераторы, указывающие на удаленный элемент, но, возможно, и другие итераторы! Поэтому удаление из этих контейнеров при повторении через них еще более проблематично.
Нет. В примере код недействителен itr
, вызывая поведение undefined. Но это сработало бы:
for (std::list<int>::iterator itr = lst.begin(); itr != lst.end(); )
{
if (*itr > 10)
itr = lst.erase(itr);
else
++itr;
}
Нет, вы не можете.
Но вы можете (и должны) использовать std::remove_if
вместе с функтором, который говорит "больше 10", например:
#include <list>
#include <algorithm>
int main()
{
std::list<int> lst;
lst.push_back(1);
lst.push_back(12);
lst.push_back(1);
//....
lst.erase(std::remove_if(lst.begin(), lst.end(), std::bind2nd(std::greater<int>(), 10)), lst.end());
}
Другой, более общий способ сделать это - написать собственный собственный функтор. Вот функтор is_a_match
, который возвращает true
, если проверяемое значение больше 10. Вы можете переопределить operator()
, чтобы вернуть true
, чтобы соответствовать тому, что в вашем случае означает "соответствовать":
#include <list>
#include <algorithm>
#include <functional>
struct is_a_match : public std::unary_function<int, bool>
{
is_a_match(int val) : val_(val) {};
bool operator()(int victim) const { return victim > val_; }
private:
int val_;
};
int main()
{
std::list<int> lst;
lst.push_back(1);
lst.push_back(12);
lst.push_back(1);
//....
lst.erase(std::remove_if(lst.begin(), lst.end(), is_a_match(10) ));
}
Если у вас есть преимущество с компилятором С++ 0x, вы также можете использовать lambdas, что позволяет избавиться от функтора и писать во многих случаях более выразительный код.
#include <list>
#include <algorithm>
int main()
{
std::list<int> lst;
lst.push_back(1);
lst.push_back(12);
lst.push_back(1);
//....
lst.erase(std::remove_if(lst.begin(), lst.end(), [](int v) {return v > 10;}));
}
Я думаю, что вы можете, но вам нужно переназначить итератор после удаления элемента, который можно сделать с помощью метода erase
вместо этого remove
.
В противном случае это будет небезопасно и не должно быть сделано.
См. http://www.cppreference.com/wiki/iterator/start для описания итераторов.
Несколько примечаний:
++itr
) вместо оператора post-increment (itr++
)