Мне нужно стереть элементы разных контейнеров stl и boost с помощью итератора. Иногда мне также нужно сделать это с помощью reverse_iterator, поэтому я хотел обернуть это в универсальную функцию (set).
Согласно этому:
Правила аннулирования итераторов в основном это должно быть возможно.
Что я получил так далеко, это:
template<class T, bool T_IterReturned = helpers::EraseReturnsIterator<T>::value>
struct EraseImpl
{
typedef typename T::iterator iterator;
typedef typename T::const_iterator const_iterator;
static iterator erase(list& container, iterator it) {
return container.erase(it);
}
static const_iterator erase(list& container, const_iterator it) {
return container.erase(it);
}
};
template<class T>
struct EraseImpl<T, false>
{
// This one gets used for e.g. std::set whos erase does not return
// an iterator until C++11
typedef typename T::iterator iterator;
typedef typename T::const_iterator const_iterator;
static iterator erase(list& container, iterator it) {
container.erase(it++);
return it;
}
static const_iterator erase(list& container, const_iterator it) {
container.erase(it++);
return it;
}
};
template<typename T>
inline typename T::iterator erase(T& container, typename T::iterator it)
{
return detail::EraseImpl<T>::erase(container, it);
}
template<typename T>
inline typename T::reverse_iterator erase(T& container, typename T::reverse_iterator it)
{
typename T::reverse_iterator tmp = it;
return typename T::reverse_iterator(erase(container, (++tmp).base()));
}
Это должно работать в большинстве случаев, но, например, вектороподобный контейнер, который не возвращает итератор, сломал бы это. Наборы не делают недействительными любые другие итераторы -> хорошо, чтобы использовать следующий итератор. Для векторов мне нужно сохранить предыдущий итератор (если есть) и вернуть его. Для deque (и аналогичных) без возврата итератора это не будет работать вообще. Я не хочу реализовывать EraseImpl для всех известных контейнеров, например, это потребовало бы, чтобы я включил все их заголовки, которых я хочу избежать.
Есть ли что-нибудь, что я могу избежать, чтобы специализировать его для всех типов? Конечно, я могу создать признак с перечислением, подобным {Use_Next, Use_Prev}, и оставить его для специализированных контейнеров, делающих недействительными все итераторы. Но опять же: я не хочу включать все возможные заголовки.
Решение, которое я сейчас использую, заключается в использовании класса свойств, который может быть специализирован для каждого класса контейнера.
По умолчанию это «Не разрешено», но оно также предоставляет специализации для контейнеров, которые имеют функцию стирания, которая возвращает итератор. Это делается в общем виде, поэтому проверяется только наличие такой функции. Если он найден, его использует generic_erase. Если нет, то эта черта спрашивает специализированный пользователь, что erase делает с итераторами (next_iterator_valid, prev_iterator_valid, all_invalid) и generic_erase действует соответственно.
Это было полезно для этой задачи: Проверьте подпись функции также для унаследованных функций