Вставить в очередь 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 - это тоже не удалось, на этот раз я сказал, что «ссылка» не является членом «std :: queue». std::back_inserter и std::back_insert_iterator также выходят из строя с той же ошибкой.

Я упустил что-то очевидное, или insert_iterators просто не работают с очередями?


person Andy Balaam    schedule 12.11.2009    source источник
comment
Хотя ответы, которые вам были даны, хороши, лично я бы просто избегал std :: queue и любого другого поврежденного адаптера контейнера.   -  person Kylotan    schedule 12.11.2009
comment
Да, предложение SBI и Naveen об использовании двухсторонней очереди было бы хорошей альтернативой.   -  person Andy Balaam    schedule 13.11.2009


Ответы (9)


К сожалению, 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 ) );
person CB Bailey    schedule 12.11.2009
comment
Гений. Аккуратно, красиво и демонстрирует странное решение использовать имя push вместо push_back в первую очередь. - person Andy Balaam; 12.11.2009
comment
Это не push_back, потому что точка, в которой происходит вставка, концептуально не имеет значения. Кроме того, что push_back значило бы для priority_queue? - person UncleBens; 12.11.2009
comment
Концептуально это имеет смысл, я просто использовал работу «к сожалению», потому что это означает, что она закрывается с помощью back_insert_iterator, что может быть действительно полезно. - person CB Bailey; 12.11.2009
comment
Я согласен с комментарием Килотана к вопросу. Если вы хотите делать что-то, выходящее за рамки того, что позволяет ADT очереди (в основном, нажимать и выталкивать), бессмысленно бороться с адаптером очереди. - Хотя +1 к твоему элегантному ответу. - person UncleBens; 12.11.2009

Очередь не допускает итерацию по своим элементам.

Из 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 глуп.

person Frank Krueger    schedule 12.11.2009
comment
Энди пытается не повторять очередь, а добавлять к ней. - person sbi; 12.11.2009
comment
Да, но мне нужна вещь, похожая на итератор, чтобы иметь возможность вставлять в нее. - person Andy Balaam; 12.11.2009
comment
В остальном ваш ответ разумный. :) +1 - person sbi; 12.11.2009
comment
@sbi, важно, чтобы вы понимали, что итераторы в C ++ делают намного больше, чем просто итерации. Когда они говорят, что это не позволяет итерацию, они говорят гораздо больше, чем вы не можете перечислить элементы. - person Frank Krueger; 12.11.2009
comment
Почему бы не реализовать queue_inserter, чтобы он соответствовал интерфейсу back_insert_iterator? std::copy не «глупый» (ИМХО!), Он просто требует итератора вывода, который не является обременительным интерфейсом. - person CB Bailey; 12.11.2009
comment
обременительный? Шутки в сторону? Мой класс отлично реализует OutputIterator. Тот факт, что copy требует, чтобы я наследовал std::iterator, это просто утечка в абстракции. char* наследуется от std::iterator? - person Frank Krueger; 12.11.2009
comment
Ваш queue_inserter не может быть назначен, но помимо этого у вас нет необходимости для наследования от iterator, вам нужно только указать специализацию для iterator_traits, если вы хотите использовать свой итератор со стандартными алгоритмами. - person CB Bailey; 12.11.2009
comment
char* не наследуется от iterator, но есть специализация для iterator_traits<T*>. Наследование от iterator - это самый простой способ заставить iterator_traits распознать ваш итератор. - Возможно, вы поймете ценность, если попытаетесь реализовать какой-то алгоритм, который должен знать value_type (auto в C ++ 0x, вероятно, значительно упрощает работу в этом отделе) или по-разному обрабатывать итераторы с произвольным доступом и двунаправленные итераторы. - person UncleBens; 13.11.2009
comment
Кстати, std::copy, в частности, обычно нужно знать больше об итераторах, чтобы он мог выбрать оптимальный способ сделать это: например, указатели + POD могут быть перемещены в память. - person UncleBens; 13.11.2009

std::queue не является контейнером в смысле STL, это контейнер адаптер с очень ограниченной функциональностью. Для того, что вам кажется необходимым, либо std::vector, либо std::deque («двусторонняя очередь, которая является« настоящим контейнером »), кажется правильным выбором.

person sbi    schedule 12.11.2009
comment
Или используйте цикл for для помещения в очередь. Но то, что я делаю, не кажется необоснованным, не так ли? - person Andy Balaam; 12.11.2009
comment
Нет, это не так. См. Ответ Фрэнка о том, как достичь желаемого. - person sbi; 12.11.2009
comment
Нет, это не так (функционально), но, к сожалению, вам придется свернуть свой собственный инсертер. - person Matthieu M.; 12.11.2009

Я почти уверен, что это просто не сработает - очередь предоставляет push, но итератор вставки предполагает использовать push_front или push_back. Нет никакой реальной причины, по которой вы не могли бы написать свое собственное push_insert_iterator (или любое другое имя, которое вы предпочитаете), но это немного больно ...

person Jerry Coffin    schedule 12.11.2009

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;
};

Если такая вещь еще не существует, но я почти уверен, что это не так.

person Mike Seymour    schedule 12.11.2009
comment
Я как раз собирался принять этот ответ, который выглядит превосходно (хотя я его не пробовал), но Чарльз протянул вам сообщение. - person Andy Balaam; 12.11.2009
comment
Было бы неплохо иметь такой итератор в Стандартной библиотеке. Однако, что касается вашей реализации, я думаю, было бы безопаснее, чтобы оператор разыменования возвращал прокси-объект вместо текущего объекта. Действительно, код в том виде, в каком он есть, позволяет вам делать push_iterator< queue< int > > it; it = 42;, что неверно (он также позволяет вам делать ******it, что не более правильно). operator* должен возвращать объект, определяющий operator=. - person Luc Touraille; 17.11.2011
comment
Вы также можете добавить к своему ответу обычную вспомогательную функцию, позволяющую опустить тип контейнера: template < typename Container > push_iterator< Container > pusher( Container & c ) { return push_iterator< Container >( c ); }. - person Luc Touraille; 17.11.2011

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);
person Naveen    schedule 12.11.2009

Вам нужен push_inserter (то есть устройство для вставки, которое выполняет pushes в очередь). Насколько мне известно, такого итератора в STL нет. Что я обычно делаю, так это, к сожалению, возвращаюсь к старому доброму циклу for.

Если у вас хватит смелости, вы можете создать свой собственный итератор, например:

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).

person Luc Touraille    schedule 12.11.2009
comment
Он называется обратно в STL, и вы получаете его от back_inserter. Однако на std::queue это не работает. - person sbi; 12.11.2009
comment
Я знаю, что такое back_insert_iterator: итератор, который выполняет push_back в контейнер. В очереди нет метода push_back, поэтому back_insert_iterator не работает. Метод добавления элементов в очередь - push, отсюда и идея push_insert_iterator ... - person Luc Touraille; 12.11.2009
comment
@sbi: Учитывая, что вставка элемента в очередь производится через функцию queue :: push (), push_inserter - неплохая репутация, ИМХО - person Éric Malenfant; 12.11.2009
comment
@ Эрик: Нет, это не так. Нисколько. Однако мой комментарий был написан до того, как в ответе Люка когда-либо было push_inserter. :) - person sbi; 13.11.2009

В этом простом случае вы можете написать:

vector<int> v;
v.push_back( 1 );
v.push_back( 2 );

queue<int, vector<int> > q(v);

Это сделает копию vector и будет использовать ее как базовый контейнер queue.

Конечно, этот подход не сработает, если вам нужно поставить вещи в очередь после того, как очередь была построена.

person Thomas    schedule 12.11.2009
comment
Очень хороший момент. К сожалению, мой пример был слишком упрощен, и я не хотел этого делать. - person Andy Balaam; 12.11.2009
comment
очередь не может использовать вектор. Контейнер должен поддерживать pop_front. - person UncleBens; 12.11.2009
comment
Ага! Ты прав. Из-за шаблонов мой тест этого не показал. Что ж, я думаю, вы все еще можете создать очередь, которая позволит вам проверять front() и _2 _...: P - person Thomas; 12.11.2009

для 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); }  );
person E_g    schedule 05.09.2019