Почему в С++ std::vector нет метода pop_front?

Почему в С++ нет метода pop_front std::vector?


person Alessandro Jacopson    schedule 06.04.2011    source источник


Ответы (8)


Потому что std::vector не имеет особой функции вставки элементов спереди, в отличие от некоторых других контейнеров. Функциональность, предоставляемая каждым контейнером, имеет смысл для этого контейнера.

Вероятно, вам следует использовать std::deque, который явно хорош при вставке спереди и сзади.

Ознакомьтесь с этой схемой.

person Lightness Races in Orbit    schedule 06.04.2011
comment
Хорошая диаграмма, но это все черное, трудно понять - person HumbleDeveloper; 23.11.2020
comment
На самом деле это не объясняет, почему нет метода pop_front(), он говорит только о вставке. Как вставка элементов связана с извлечением элементов? - person Rasmus Dall; 17.12.2020
comment
было бы неплохо, если бы можно было исправить схему. - person cpchung; 03.01.2021

Хотя это неэффективно для больших векторов, следующее эквивалентно pop_front() для std::vector

vec.erase(vec.begin());

Как указано в других ответах, std::vector не предназначен для удаления первого элемента и потребует перемещения/копирования всех оставшихся элементов. В зависимости от конкретного варианта использования может быть удобно рассмотреть другие контейнеры.

person SEGStriker    schedule 29.12.2014
comment
Это работает, но не будет эффективно на больших векторах. - person Alexis Wilke; 23.06.2019
comment
какова будет временная сложность этого кстати? - person Swastik Singh; 07.08.2021

vector обычно реализуется примерно так:

struct 
{
  T* begin; // points to the first T in the vector
  T* end; // points just after the last T in the vector
  int capacity; // how many Ts of memory were allocated
};

«begin» выполняет двойную функцию: «указатель на первую T в векторе» и «указатель на всю выделенную нами память». поэтому невозможно «вытолкнуть» элементы из передней части вектора, просто увеличив «начало» — сделайте это, и у вас больше не будет указателя на память, которую нужно освободить. это приведет к утечке памяти. поэтому «pop_front» должен будет скопировать все T из задней части вектора в переднюю часть вектора, а это сравнительно медленно. поэтому они решили оставить его вне стандарта.

то, что вы хотите, это что-то вроде этого:

struct 
{
  T* allocated; // points to all the memory we allocated
  T* begin; // points to the first T in the vector
  T* end; // points just after the last T in the vector
  int capacity; // how many Ts of memory were allocated
};

при этом вы можете «pop_front», перемещая «begin» вперед и назад, не опасаясь забыть, какую память нужно освободить позже. почему std::vector не работает таким образом? я предполагаю, что это был вопрос вкуса тех, кто писал стандарт. их цель, вероятно, заключалась в том, чтобы предоставить максимально простой «динамически изменяемый массив», и я думаю, что им это удалось.

person Community    schedule 06.04.2011
comment
Не только «вкус». Представьте себе все произвольные решения: толкание спереди исчерпывает свободное пространство в голове. Перераспределить или просто переместить элементы, пока в конце не останется свободного места, если оно вообще есть? Точно так же, толкая в конце, мы движемся вперед или перераспределяем? Простое перемещение кажется расточительным, но перераспределение затрат. Сделать его циклическим и войти в ад вычисления индекса? Потом емкость. Резерв(100), куда поместить первый элемент? фронт? центр? Почему? Давайте вытолкнем некоторые элементы, теперь он пуст, начнем с конца, мы скорректируем их вперед, в центр, конец или ничего не сделаем? Слишком много для настройки через type traits/etc. - person quetzalcoatl; 14.08.2020

Потому что push_back и pop_back — это специальные операции для вектора, которые требуют только O(1) вычислений. Любой другой толчок или толчок занимает O(n).

Это не "баг" и не "причуда", это просто свойство векторного контейнера. Если вам нужен быстрый pop_front, подумайте о переходе на другой контейнер.

person orlp    schedule 06.04.2011
comment
В частности, рассмотрите возможность перехода на std::deque<>, если вам нужен быстрый pop_front(). - person ildjarn; 06.04.2011
comment
требуется только вычисление O (1) - амортизируется O (1) в случае push_back, в худшем случае - Theta (n), когда он должен перераспределяться, потому что вы не зарезервировали пространство. - person Steve Jessop; 06.04.2011
comment
Я пропустил выделение в этом примере, потому что пользователь говорил о всплывающих окнах. Вы правы, хотя. - person orlp; 06.04.2011
comment
Естественно, это должно было бы сделать перераспределение, но почему это будет O (n) по сравнению с перераспределением «push_back» - person deceleratedcaviar; 06.04.2011
comment
@Daniel: Потому что push_back не всегда нужно перераспределять. - person Lightness Races in Orbit; 25.11.2012
comment
Почему pop_front не может быть O(1)? Вы бы просто продвинули указатель на начало массива на один элемент? - person Jonathan.; 01.09.2016
comment
@Джонатан. Можно, но не используя ту же реализацию, что и обычный вектор, там больше логики. См. github.com/orlp/devector (реализация еще не закончена, но концепция есть). - person orlp; 01.09.2016
comment
@Джонатан. Что-то вроде deque делает это, но конечный результат заключается в том, что он должен работать немного по-другому, если вы не хотите, чтобы вся память вашего компьютера была выделена, но больше не использовалась... поэтому существует deque... вот почему у deque есть pop_front. - person Lightness Races in Orbit; 24.07.2018

Однако, если вам нужен pop_front и НЕ заботится об индексе элементов в векторе, вы можете сделать своего рода pop_front с чем-то вроде

template<typename T>
void pop_front(std::vector<T>& vec)
{
   vec.front() = vec.back();
   vec.pop_back();
}

Дэн Хиггинс тоже говорит об этом: https://youtu.be/oBbGC-sUYVA?t=2m52s< /а>

person marchelbling    schedule 06.04.2011
comment
Это был бы странный случай, когда вы оба заботились и не заботились о порядке элементов. Потому что, если вам на самом деле все равно, это еще проще: template‹typename T› void pop_front(std::vector‹T›& v) { v.pop_back(); } - person MSalters; 06.04.2011
comment
Ваш комментарий кажется мне неправильным. Выполняя template<typename T> void pop_front(std::vector<T>& v) { v.pop_back(); }, вы не удаляете ожидаемый элемент front. Я предлагаю поменять местами элементы front и back и удалить последний. - person marchelbling; 02.11.2014
comment
Кроме того, использование вектора, когда вы не очень заботитесь о порядке элементов, может произойти, если вы хотите манипулировать непрерывным буфером памяти. Я не спорю, хорошая это идея или нет, но в этом очень конкретном (и указанном) случае это правильный способ сделать pop_front в O (1). - person marchelbling; 02.11.2014
comment
довольно умно, но в основном бесполезно :) +1 от меня - person Innocent Bystander; 13.07.2017
comment
@marchelbling Я думаю, что это O (2), так как у вас есть копия + стирание. Я думаю, что это умный способ сделать это, если вы используете вектор только для хранения N элементов, а не для хранения N элементов в определенном порядке. - person Alexis Wilke; 23.06.2019
comment
@AlexisWilke действительно это 2 операции O (1), что эквивалентно O (1) или, другими словами, независимо от размера вектора, мы можем выполнить pop_front за постоянное время. - person marchelbling; 25.06.2019
comment
Спасибо за ваш комментарий. Как выделено жирным шрифтом в моем первоначальном ответе, я просто предоставляю O (1) удаление векторного элемента. Как следует из вашего ответа, на самом деле это может быть для любой позиции в векторе. Опять же, я не говорю, что это хорошая идея, но я просто не согласен, если люди предполагают, что порядок векторов обязательно важен. Удобство кэширования также может быть важным. - person marchelbling; 16.08.2020

Вероятно, потому что это было бы монументально медленно для больших векторов.

pop_front() для вектора, содержащего 1000 объектов, потребуется 999 operator=() вызовов.

person Roddy    schedule 06.04.2011
comment
Не медленнее, чем erase(v.begin()). Это потому, что векторы не имеют специальных свойств для нажатия в начале, в отличие от нажатия в конце (или выталкивания). - person orlp; 06.04.2011
comment
@nightcracker - Да, стереть () одинаково плохо. И я согласен с тем, что использование pop_front() для вектора — это хороший «запах» того, что вы используете неправильный тип контейнера! - person Roddy; 06.04.2011

Vector выглядит как контейнер стека, у нас есть стек для хранения данных. Так что мы просто pop_back не pop_front. Если вы хотите pop_front, std::list может быть лучшим выбором для вас. Потому что это двунаправленная структура очереди.

person Ezra Lin    schedule 26.02.2021

#define push_front(v,val) v.insert(v.begin(), 1, val);
#define pop_front(v)  if(!v.empty())v.erase(v.begin());

Вы можете напрямую написать это и использовать это

push_front(vec,val);
pop_front(vec);
person Shubham Kumar Gupta Ggps    schedule 28.06.2021