Как работает оператор end(), какое именно значение он возвращает, и почему end()-- работает как-то странно,с second программа зависает?
map<int,string> h;
h[1]="q";
h[20]="d";
h[3]="s";
h[90]="l";
h[9]="f";
map<int,string>::iterator it;
for(it=h.begin();it!=h.end();++it)
cout<<it->first<<"\t";
cout<<"\n"<<h.end()-- ->first;
Ответы (1 шт):
Функция end возвращает итератор на несуществующий элемент, следующий за последним элементом контейнера. Он нужен только для сравнения с ним текущего итератора в цикле. Разыменовывать его нельзя.
У вас ошибка в применении оператора декремента. Оператор постфиксного декремента создаёт копию операнда (в вашем случае итератора end), затем уменьшает исходный объект (не копию) на единицу, а затем возвращает ранее созданную копию. К копии не применялась операция уменьшения, поэтому значение, возвращённое конструкцией h.end()-- - это тот же самый итератор end. К нему нельзя применять операцию разыменовывания.
Что делать? Использовать операцию префиксного декремента или функцию std::prev:
#include <iostream>
#include <map>
using namespace std;
int main()
{
map<int,string> h;
h[1] = "q";
h[20] = "d";
h[3] = "s";
h[90] = "l";
h[9] = "f";
map<int,string>::iterator it;
for (it = h.begin(); it != h.end(); ++it)
cout<< it->first << "\t" << it->second << endl;
cout << (--h.end()) -> first << endl;
cout << (--h.end()) -> second << endl;
//cout<<"\n"<< std::prev(h.end())->first;
//cout<<"\n"<< std::prev(h.end())->second;
return 0;
}
Функция std::prev хороша ещё тем, что она может найти и возвратить предыдущий итератор независимо от того, может ли оператор декремента применяться к rvalue. Например, в этом коде декремент работать не будет:
int main()
{
int a[100] = {0};
//auto last = --std::end(a); //Ошибка: оператор декремента не определён для данного типа rvalue.
auto last = std::prev(std::end(a));
cout << *last;
return 0;
}
Если же декремент для итератора не определён вообще, то функция std::prev не поможет. Например, возьмём итератор контейнера std::forward_list:
#include <iostream>
#include <forward_list>
using namespace std;
int main()
{
std::forward_list<int> list;
list.push_front(1);
//auto last = std::end(list);
//--last;
auto last = std::prev(std::end(list));
cout << *last;
return 0;
}
Некоторые компиляторы могут такое проглотить, но работать это не будет. Например, x86_64 gcc 12.2 успешно компилирует, но программа вылетает с ошибкой. А Visual Studio при компиляции выдаёт такую ошибку:
static_assert failed: 'prev requires bidirectional iterator'