Вставить в очередь 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
просто не работает с очередями?
8 ответов
К несчастью std::queue
"адаптирует" функцию, известную как push_back
чтобы просто push
что означает, что стандарт back_insert_iterator
не работает
Вероятно, самый простой способ (хотя и концептуально уродливый) состоит в том, чтобы адаптировать контейнерный адаптер с помощью адаптера адаптера контейнера с коротким сроком службы [sic] (эх!), Который живет столько же, сколько итератор с обратной вставкой.
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 ) );
Очередь не позволяет выполнять итерацию по ее элементам.
Из документов SGI STL:
Очередь - это адаптер, который предоставляет ограниченное подмножество функций контейнера. Очередь - это структура данных "первым пришел - первым обслужен" (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 глупо.
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;
};
Если такая вещь уже не существует, но я уверен, что это не так.
Я уверен, что это просто не будет работать - очередь обеспечивает push
, но итератор вставки ожидает использовать push_front
или же push_back
, Там нет реальной причины, вы не могли бы написать свой собственный push_insert_iterator
(или любое другое имя, которое вы предпочитаете), но это немного больно...
std::queue
это не контейнер в смысле STL, это адаптер контейнера с очень ограниченной функциональностью. Для того, что вам, кажется, нужно std::vector
или же std::deque
("двусторонняя очередь, которая является" реальным контейнером "), кажется правильным выбором.
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);
То, что вам нужно, это push_inserter
(т.е. вставщик, который выполняет push
в очередь). Насколько я знаю, в 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
).
Для 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); } );
В этом простом случае вы можете написать:
vector<int> v;
v.push_back( 1 );
v.push_back( 2 );
queue<int, vector<int> > q(v);
Это сделает копию vector
и использовать его в качестве основного контейнера queue
,
Конечно, этот подход не будет работать, если вам нужно ставить вещи в очередь после того, как очередь будет построена.