2016-05-24 8 views
2

У меня есть std::list<some_object> events, и я хотел бы удалить все элементы этого списка, кроме последнего. Так я думал, что делать (как это было предложено this thread):C++ List erase to end() - 1 элемент в списке

std::list<some_object>::iterator it = events.begin(); // Init iterator on top of list 
while(it!=*std::advance(events.end(),-1)) events.erase(it++); 

К сожалению, выше не работает, как он производит ошибку:

error: invalid initialization of non-const reference of type ‘std::_List_iterator<node_info>&’ from an rvalue of type ‘std::__cxx11::list<node_info>::iterator {aka std::_List_iterator<node_info>}’ 
      while(it!=*std::advance(event_heap.end(),-1)){ 

Но не list::end должна возвращать итератор? Что я делаю не так?

+1

Почему бы не использовать 'std :: list :: erase'? – NathanOliver

+1

Что не так с 'events.erase (events.begin(), - (events.end()));'? – Beta

+1

Вы должны использовать 'std :: list :: erase()', но в качестве альтернативы, возможно, вы можете 'push_back()' элемент 'back()' в temp 'std :: list', а затем' std :: swap() 'temp с вашим основным' std :: list', например: 'std :: list temp; temp.push_back (events.back()); std :: swap (события, temp); 'Когда temp затем выходит за рамки, он освободит все элементы, которые вы не сохранили. –

ответ

1

But, isn't list::end supposed to return an iterator?

Да, но std::advance принимает неконстантные ссылок в качестве 1-го параметра, в то время как event_heap.end() временных переменной и не может быть связаны с неконстантной ссылкой.

И std::advance возвращает ничего (т.е. void), так что вы не можете использовать operator* на нем или сравнить его с it.

Прямое крепление будет как:

std::list<some_object>::iterator it = events.begin(); 
auto one_before_end = events.end(); 
std::advance(one_before_end, -1); // or --one_before_end; 
while (it != one_before_end) events.erase(it++); 

КСТАТИ: std::list::erase имеет перегрузку, принимая диапазон итератора, так что вы можете просто:

events.erase(events.begin(), one_before_end); 
+1

Спасибо @songyuanyao и все за ваши просветительские ответы! @NathanOliver @Beta: Я забыл упомянуть, что перед стиранием мне нужно сделать некоторые дополнительные операции над отдельными элементами списка. Поэтому мне нужно стирать элементы списка один за другим. Следовательно, цикл 'while', вероятно, является самым прямым способом сделать это. В качестве альтернативы я мог бы использовать предикат функтора с 'std :: list :: remove_if' ... – maurizio

6

Это один типичное использование std::prev. Если вы хотите remove все элементы, кроме последнего, самый идиоматический способ сделать это - использовать std::prev (который в основном использует std::advance) на вашем концевом итераторе.

myList.erase(myList.begin(), std::prev(myList.end())); 

Пример:

#include <iostream> 
#include <iterator> 
#include <list> 

int main(){ 
    std::list<int> ls = {3, 5, 9, 2}; 

    if(!ls.empty()) 
     ls.erase(ls.begin(), std::prev(ls.end())); 

    for(auto x : ls) 
     std::cout << x << std::endl; 

    return 0; 
} 

Печать:

2 

Как Remy Лебо отметил в комментарии, для не C++ 11 компилятором, вы можете просто использовать std::advance(), как описано в songyuanyao's answer

+0

Обратите внимание, что' std :: prev() 'является новым в C++ 11. Для более ранних версий вам необходимо использовать 'std :: advance()'. –

+0

@RemyLebeau, Спасибо, исправлено. :-) – WhiZTiM

+0

В общем вы должны проверить '! Ls.empty()' перед выполнением 'prev (ls.end())' или эквивалент –

 Смежные вопросы

  • Нет связанных вопросов^_^