Вставка в очередь STL с использованием std:: copy
Я хотел бы использовать std::copy
для вставки элементов в очередь следующим образом:
vector<int> v;
v.push_back( 1 );
v.push_back( 2 );
queue<int> q;
copy( v.begin(), v.end(), insert_iterator< queue<int> >( q, q.front() ) );
Но это не компилируется, жалуясь, что begin
не является членом std::queue
.
Примечание: я тоже попробовал это с std::inserter
- это тоже не удалось, на этот раз я сказал, что 'reference' не является членом 'std::queue'. std::back_inserter
и std::back_insert_iterator
также завершаются с той же ошибкой.
Я что-то упускаю из виду, или insert_iterator
просто не работает с очередями?
Ответы
Ответ 1
К сожалению, std::queue
"адаптирует" функцию, известную как push_back
, к просто push
, что означает, что стандартный back_insert_iterator
не работает.
Вероятно, самым простым способом (хотя и концептуально уродливым) является адаптация адаптера контейнера с адаптером адаптера адаптера с коротким замыканием [sic] (eugh!), который до тех пор, пока используется итератор задней вставки.
template<class T>
class QueueAdapter
{
public:
QueueAdapter(std::queue<T>& q) : _q(q) {}
void push_back(const T& t) { _q.push(t); }
private:
std::queue<T>& _q;
};
Используется следующим образом:
std::queue<int> qi;
QueueAdapter< std::queue<int> > qiqa( qi );
std::copy( v.begin(), v.end(), std::back_inserter( qiqa ) );
Ответ 2
Очередь не позволяет итерации через свои элементы.
Из SGI STL Docs:
Очередь - это адаптер, который обеспечивает ограниченное подмножество контейнера Функциональность Очередь - это "первая в (FIFO). 1То есть, элементы добавляются к назад очереди и может быть удален с фронта; Q.front() - это элемент, добавленный в очередь в последнее время. Очередь не позволяет итерации через его элементы. [2]
Вы можете выполнить эту работу, но вы не можете использовать insert_iterator
. Вам нужно написать что-то вроде queue_inserter
, которое представляет интерфейс итератора.
Обновление. Я не мог с собой поделать и попытаться реализовать итератор, в котором вы нуждаетесь. Вот результаты:
template< typename T, typename U >
class queue_inserter {
queue<T, U> &qu;
public:
queue_inserter(queue<T,U> &q) : qu(q) { }
queue_inserter<T,U> operator ++ (int) { return *this; }
queue_inserter<T,U> operator * () { return *this; }
void operator = (const T &val) { qu.push(val); }
};
template< typename T, typename U >
queue_inserter<T,U> make_queue_inserter(queue<T,U> &q) {
return queue_inserter<T,U>(q);
}
Это отлично работает для таких функций:
template<typename II, typename OI>
void mycopy(II b, II e, OI oi) {
while (b != e) { *oi++ = *b++; }
}
Но это не работает с копией STL, потому что STL глуп.
Ответ 3
std::queue
не является контейнером в смысле STL, это контейнерный адаптер с очень ограниченными функциями. Для того, что вам кажется, вам нужно либо std::vector
, либо std::deque
( "двойная очередь", которая является "реальным контейнером" ), кажется правильным выбором.
Ответ 4
Я уверен, что это просто не сработает - очередь предоставляет push
, но итератор вставки ожидает использовать push_front
или push_back
. Нет никакой реальной причины, по которой вы не могли бы написать свой собственный push_insert_iterator
(или любое другое имя, которое вы предпочитаете), но это немного боль...
Ответ 5
insert_iterator
и back_insert_iterator
работают только с контейнерами (или адаптерами) с (соответственно) insert
и push_back
методами - queue
. Вы можете написать свой собственный итератор, смоделированный на них, примерно так:
template <typename Container>
class push_iterator : public iterator<output_iterator_tag,void,void,void,void>
{
public:
explicit push_iterator(Container &c) : container(c) {}
push_iterator &operator*() {return *this;}
push_iterator &operator++() {return *this;}
push_iterator &operator++(int) {return *this;}
push_iterator &operator=(typename Container::const_reference value)
{
container.push(value);
return *this;
}
private:
Container &container;
};
Если такое уже не существует, но я уверен, что этого не делает.
Ответ 6
Вам понадобится push_inserter
(т.е. вставка, которая выполняет push
es в очередь). Насколько мне известно, такого итератора в STL нет. То, что я обычно делаю, грустно возвращается к старой доброй петле.
Если у вас есть смелость, вы можете перевернуть свой собственный итератор, что-то вроде этого:
template <typename Container>
class push_insert_iterator
{
public:
typedef Container container_type;
typedef typename Container::value_type value_type;
explicit push_insert_iterator(container_type & c)
: container(c)
{} // construct with container
push_insert_iterator<container_type> & operator=(const value_type & v)
{
//push value into the queue
container.push(v);
return (*this);
}
push_insert_iterator<container_type> & operator*()
{
return (*this);
}
push_insert_iterator<container_type> & operator++()
{
// Do nothing
return (*this);
}
push_insert_iterator<container_type> operator++(int)
{
// Do nothing
return (*this);
}
protected:
container_type & container; // reference to container
};
template <typename Container>
inline push_insert_iterator<Container> push_inserter(Container & c)
{
return push_insert_iterator<Container>(c);
}
Это всего лишь черновик, но у вас есть идея. Работает с любым контейнером (или, допустимо, контейнерными адаптерами) с помощью метода push
(например, queue
, stack
).
Ответ 7
std::queue
не является одним из основных контейнеров в STL. Это контейнерный адаптер, который построен с использованием одного из базовых контейнеров STL (в этом случае один из последовательных контейнеров: std::vector
std::deque
или std::list
). Он разработан специально для поведения FIFO и не обеспечивает случайную вставку на заданном итераторе, который вы хотите для работы insert_iterator
. Следовательно, будет невозможно использовать такую очередь.
Самый простой способ, который я мог бы придумать, - это:
class PushFunctor
{
public:
PushFunctor(std::queue<int>& q) : myQ(q)
{
}
void operator()(int n)
{
myQ.push(n);
}
private:
std::queue<int>& myQ;
};
И используйте его как:
queue<int> q;
PushFunctor p(q);
std::for_each(v.begin(), v.end(), p);
Ответ 8
В этом простом случае вы можете написать:
vector<int> v;
v.push_back( 1 );
v.push_back( 2 );
queue<int, vector<int> > q(v);
Это сделает копию vector
и использует ее как базовый контейнер queue
.
Конечно, этот подход не будет работать, если вам нужно вставить объекты после создания очереди.
Ответ 9
для c++ 11
std::for_each( v.begin(), v.end(), [&q1](int data) { q1.push(data); } );
и c ++ 14
std::for_each( v.begin(), v.end(), [&q1](auto data) { q1.push(data); } );