Здравствуйте, Sm0ke, Вы писали:
S>Здравствуйте, sergii.p, Вы писали:
SP>>Здравствуйте, Igore, Вы писали:
I>>>Возьми после первой вставки rbegin
SP>>и после второй вставки итератор может быть не валиден
S>std::list не инвалидирует итераторы и ссылки при добавлении, если верить документации.
Совершенно верно, инвалидации итератора не будет.
Однако работать "как надо" это не будет все равно. `rbegin()` вернет reverse iterator, базирующийся на `end()`. И в процессе добавления элементов в список, полученный итератор будет продолжать базироваться на `end()` итераторе (который тоже не инвалидируется и остается неизменным в процессе добавления). То есть такой итератор будет "прилеплен" к концу списка. И после завершения добавления полученный из `rbegin()` reverse iterator будет ссылаться на последний вставленный элемент, а не на первый.
#include <list>
#include <iostream>
int main()
{
std::list<int> l = { 1, 2, 3 };
auto it = l.rbegin();
std::cout << *it << std::endl;
l.push_back(4);
std::cout << *it << std::endl;
l.push_back(5);
std::cout << *it << std::endl;
}
Получим
3
4
5
Best regards,
Андрей Тарасевич
Re[3]: Итератор на только что добавленный элемент списка
Здравствуйте, 00011011, Вы писали:
0>Здравствуйте, Андрей Тарасевич, Вы писали:
АТ>>1. Использовать `std::list<>::insert` и запомнить результат первой вставки, если таковая была
0>Да, это именно то что нужно. Вставка в позицию итератора end() работает, я как-то не подумал об этой возможности.
0>И еще вопрос: определено ли стандартом, что итератор end() одинаковый для любого состояния конкретного списка? 0>т.е.
Здравствуйте, sergii.p, Вы писали:
АТ>>Непонятен смысл использования `std::advance` в таком коде....
SP>чтобы избавится от привязки к контейнеру. Может вам кажется, что для демонстрации такой простой идеи подойдёт it + pos. Но внутренний перфекционист отговорил меня от этого.
Но в коде и так уже присутствует явная привязка к контейнеру. Код ради того и написан, чтобы воспользоваться привязкой к контейнерам, основанным на массивах и потому предоставляющих random access.
Будет только лучше, если вы воспользуетесь `+` и тем самым запретите использование вашей функции для контейнеров без random access. В противном случае неопытный пользователь может применить вашу функцию к контейнеру без random access и даже и не заметить, что тем самым он внес в код существенную неэффективность.
`std::advance` — это "костыль-заглушка", специально предназначенный для ситуаций, когда вы понимаете, что ваш код будет работать неэффективно без прямого доступа, но пока что не хотите заниматься оптимизацией (у вас нет времени и на текущем этапе вас устроит заглушка). То есть `std::advance` и `std::distance` — это функции-пометки, функции-закладки, функции-памятки, функции-красные флаги, помечающие неэффективные места, которые следует переписать в будущем.
Best regards,
Андрей Тарасевич
Re[2]: Итератор на только что добавленный элемент списка
Довольно простая задача — есть список, есть функция которая может добавить в конец этого списка произвольное число элементов (в т.ч. и ни одного).
Я хочу чтобы функция возвращала итератор на первый добавленный элемент, или end() если элементы не были добавлены — с тем, чтобы вызывающий код мог пройтись по этим новым элементам, не затрагивая старые.
Но оказалось, что push_back не возвращает ничего;
Если брать итератор end() вообще перед добавлением, то после добавления элемента он не указывает на первый добавленный элемент (что было бы логично для массивов, но разумеется не работает для списков, к сожалению).
Как красиво и эффективно решить эту задачу?
Re: Итератор на только что добавленный элемент списка
Здравствуйте, 00011011, Вы писали:
0>Довольно простая задача — есть список, есть функция которая может добавить в конец этого списка произвольное число элементов (в т.ч. и ни одного). 0>Я хочу чтобы функция возвращала итератор на первый добавленный элемент, или end() если элементы не были добавлены — с тем, чтобы вызывающий код мог пройтись по этим новым элементам, не затрагивая старые. 0>Но оказалось, что push_back не возвращает ничего; 0>Если брать итератор end() вообще перед добавлением, то после добавления элемента он не указывает на первый добавленный элемент (что было бы логично для массивов, но разумеется не работает для списков, к сожалению). 0>Как красиво и эффективно решить эту задачу?
1. Использовать `std::list<>::insert` и запомнить результат первой вставки, если таковая была
2. Всегда держать в списке один guard element в самом начале. Тогда перед вставкой можно запомнить `std::prev()` от `end()`, а после вставки (любым способом) сделать `std::next` от запомненного значения.
3. То же самое, что и 2, но не держать в списке guard element, а обрабатывать ситуацию изначально пустого списка, как особую.
4. ...
Best regards,
Андрей Тарасевич
Re[2]: Итератор на только что добавленный элемент списка
Здравствуйте, sergii.p, Вы писали:
SP>Здравствуйте, 00011011, Вы писали:
SP>у меня предубеждение против list. Всё-таки куча аллокаций на ровном месте. Почему бы просто не запомнить позицию вставленного элемента?
SP>
Здравствуйте, Андрей Тарасевич, Вы писали:
АТ>Непонятен смысл использования `std::advance` в таком коде....
чтобы избавится от привязки к контейнеру. Может вам кажется, что для демонстрации такой простой идеи подойдёт it + pos. Но внутренний перфекционист отговорил меня от этого.
Re[2]: Итератор на только что добавленный элемент списка
Здравствуйте, sergii.p, Вы писали:
SP>у меня предубеждение против list. Всё-таки куча аллокаций на ровном месте. Почему бы просто не запомнить позицию вставленного элемента?
Можно std::deque использовать.
Re[3]: Итератор на только что добавленный элемент списка
Здравствуйте, sergii.p, Вы писали:
SP>этот кастомный аллокатор называется vector
Да, но нет, хотя и да
Иногда у вектора есть минус — необходимость сдвига контента если средний элемент удаляется. На маленьких размерах это незаметно, т.к. кэш и предиктивное чтение творят чудеса. Но если размер побольше, то ой. В такой ситуации, список может дать меньше тормозов, даже не взирая на indirection и отсутствие упорядоченности внутри блока памяти. Ну и сюда же вопрос сохранности итераторов — тоже может быть полезно. В общем, думаем, взвешаваем, измеряем.
Re: Итератор на только что добавленный элемент списка
Здравствуйте, 00011011, Вы писали:
0>Довольно простая задача — есть список, есть функция которая может добавить в конец этого списка произвольное число элементов (в т.ч. и ни одного). 0>Я хочу чтобы функция возвращала итератор на первый добавленный элемент, или end() если элементы не были добавлены — с тем, чтобы вызывающий код мог пройтись по этим новым элементам, не затрагивая старые. 0>Но оказалось, что push_back не возвращает ничего;
Возьми после первой вставки rbegin
Re[2]: Итератор на только что добавленный элемент списка
Здравствуйте, sergii.p, Вы писали:
SP>Здравствуйте, Igore, Вы писали:
I>>Возьми после первой вставки rbegin
SP>и после второй вставки итератор может быть не валиден
std::list не инвалидирует итераторы и ссылки при добавлении, если верить документации.
Re[5]: Итератор на только что добавленный элемент списка
Здравствуйте, Андрей Тарасевич, Вы писали:
АТ>Однако работать "как надо" это не будет все равно. `rbegin()` вернет reverse iterator, базирующийся на `end()`. И в процессе добавления элементов в список, полученный итератор будет продолжать базироваться на `end()` итераторе (который тоже не инвалидируется и остается неизменным в процессе добавления). То есть такой итератор будет "прилеплен" к концу списка. И после завершения добавления полученный из `rbegin()` reverse iterator будет ссылаться на последний вставленный элемент, а не на первый.
Да, точно, значит надо еще раз сдвинуться чтобы вместе с rbegin не уезжать, но порядок будет обратный
#include <list>
#include <iostream>
int main()
{
std::list<int> l = { 1, 2, 3 };
l.push_back(4);
auto tmp = l.rbegin();
tmp++;
l.push_back(5);
l.push_back(6);
for( auto it = l.rbegin(); it != tmp; ++it )
{
std::cout << *it << std::endl;
}
// 6 5 4
}
Re: Итератор на только что добавленный элемент списка
Здравствуйте, 00011011, Вы писали:
0>Довольно простая задача — есть список, есть функция которая может добавить в конец этого списка произвольное число элементов (в т.ч. и ни одного). 0>Я хочу чтобы функция возвращала итератор на первый добавленный элемент, или end() если элементы не были добавлены — с тем, чтобы вызывающий код мог пройтись по этим новым элементам, не затрагивая старые. 0>Но оказалось, что push_back не возвращает ничего; 0>Если брать итератор end() вообще перед добавлением, то после добавления элемента он не указывает на первый добавленный элемент (что было бы логично для массивов, но разумеется не работает для списков, к сожалению). 0>Как красиво и эффективно решить эту задачу?
Если я правильно понимаю задачу, в списке уже есть эта функция: list::insert #4.
Ce n'est que pour vous dire ce que je vous dis.
Re[3]: Итератор на только что добавленный элемент списка
Здравствуйте, Mr.Delphist, Вы писали:
MD>Здравствуйте, sergii.p, Вы писали:
SP>>у меня предубеждение против list. Всё-таки куча аллокаций на ровном месте.
MD>Кастомный аллокатор, который делает pre-alloc достаточного размера и затем просто возвращает адреса?
Тут надо замерять по скорости нужен ли такой аллокатор? По сути операционная система сама должна выдавать эти адреса оптимально, будет ли выигрыш?
Потом при освобождении надо в аллокаторе помечать элемент, как освободившийся, чтобы потом снова его выдавать при выделении, но не может его вернуть операционной системе отдельно без всего pre-alloc блока. Стоит ли заморачиваться с такой хитровыфигованной системой, когда проще сделать в лоб?
Re[4]: Итератор на только что добавленный элемент списка
Здравствуйте, Sm0ke, Вы писали:
S>Тут надо замерять по скорости нужен ли такой аллокатор? По сути операционная система сама должна выдавать эти адреса оптимально, будет ли выигрыш? S>Потом при освобождении надо в аллокаторе помечать элемент, как освободившийся, чтобы потом снова его выдавать при выделении, но не может его вернуть операционной системе отдельно без всего pre-alloc блока. Стоит ли заморачиваться с такой хитровыфигованной системой, когда проще сделать в лоб?
Естественно, что бенчмарки наше всё в такой ситуации, но
1) Ходить к ОС ножками — явно недёшево, можно напороться на syscall. Косвенно на это намекают всякие фреймворки, которые сперва просят большой шмат памяти у системы, а затем обживаю его по надобности.
2) Само собой, ОС не знает, что там у нас с собственными внутренними аллокациями. С её точки зрения нам выдали целый кусок мяса, и вернуть лишь часть от него — никак (разве что попытаться сделать realloc на меньший размер, но тут гарантий опять же нет). Поэтому возвращать всё сразу, по ведомости ОС.
Re: Итератор на только что добавленный элемент списка
Здравствуйте, Mr.Delphist, Вы писали:
MD>Здравствуйте, Sm0ke, Вы писали:
S>>Тут надо замерять по скорости нужен ли такой аллокатор? По сути операционная система сама должна выдавать эти адреса оптимально, будет ли выигрыш? S>>Потом при освобождении надо в аллокаторе помечать элемент, как освободившийся, чтобы потом снова его выдавать при выделении, но не может его вернуть операционной системе отдельно без всего pre-alloc блока. Стоит ли заморачиваться с такой хитровыфигованной системой, когда проще сделать в лоб?
MD>Естественно, что бенчмарки наше всё в такой ситуации, но MD>1) Ходить к ОС ножками — явно недёшево, можно напороться на syscall. Косвенно на это намекают всякие фреймворки, которые сперва просят большой шмат памяти у системы, а затем обживаю его по надобности.
Так вот куда в приложении память утекла
Это оказывается фреймворки рвут куски от системы
Я конечно шучу тут, но и да, и нет.
MD>2) Само собой, ОС не знает, что там у нас с собственными внутренними аллокациями. С её точки зрения нам выдали целый кусок мяса, и вернуть лишь часть от него — никак (разве что попытаться сделать realloc на меньший размер, но тут гарантий опять же нет). Поэтому возвращать всё сразу, по ведомости ОС.
realloc — не работает
Надо определять rare case когда можно вернуть bucket (содержащий только не выданные итемы), и если он не last. Хотя после list.clear() это может быть и не так уж rare.
Сложность в чём. Вот вернули мы bucket, а он сразу опять понадобился. Значит придётся для Пула писать Trait автовозвращения Бакетов. Автоматом или по требованию.