How to convert std::queue to std::vector

耗尽温柔 提交于 2019-12-03 14:24:11

The correct container to model both queue_like behaviour and vector-like behaviour is a std::deque.

This has the advantages of:

  1. constant-time insertion and deletion at either end of the deque

  2. ability to iterate elements without destroying the deque

std::deque supports the begin() and end() methods which means you can construct a vector (with compatible value-type) directly.

#include <vector>
#include <deque>

class AcceptsVectors
{
public:
  AcceptsVectors(std::vector<double> arg);
};

int main()
{
    std::deque<double> myqueue;

    auto av = AcceptsVectors({myqueue.begin(), myqueue.end()});
}

A non-mutating conversion of a queue to a vector is not possible.

Ami Tavory

std::vector has a constructor taking a pair of iterators, so if you would be able to iterate over the queue, you would be set.

Borrowing from an answer to this question, you can indeed do this by subclassing std::queue:

template<typename T, typename Container=std::deque<T> >
class iterable_queue : public std::queue<T,Container>
{
public:
    typedef typename Container::const_iterator const_iterator;

    const_iterator begin() const { return this->c.begin(); }                                                                               
    const_iterator end() const { return this->c.end(); }
};

(Note we're allowing only const iteration; for the purpose in the question, we don't need iterators allowing modifying elements.)

With this, it's easy to construct a vector:

#include <queue>
#include <vector>

using namespace std;

template<typename T, typename Container=std::deque<T> >
class iterable_queue : public std::queue<T,Container>
{
public:
    typedef typename Container::const_iterator const_iterator;

    const_iterator begin() const { return this->c.begin(); }                                                                               
    const_iterator end() const { return this->c.end(); }
};

int main() {
    iterable_queue<int> int_queue;
    for(int i=0; i<10; ++i)
        int_queue.push(i);

    vector<int> v(int_queue.begin(), int_queue.end());
    return 0;
}

I don't think there's any direct way available. Hence this can be achieved by adding elements one by one to the vector.

std::vector<int> v;
while (!q.empty())
{
    v.push_back(q.front());
    q.pop();
}

Note that the queue will be empty after this.

As suggested by @David in the comment, it'd be good to avoid copying the queue elements (helpful especially when the contained objects are big). Use emplace_back() with std::move() to achieve the same:

v.emplace_back(std::move(q.front()));

This is just an approach to avoid copying from std::queue to std::vector. I leave it up to you, if to use it or not.

Premises

std::queue is a container adaptor. The internal container by default is std::deque, however you can set it to std::vector as well. The member variable which holds this container is marked as protected luckily. Hence you can hack it by subclassing the queue.

Solution (!)

template<typename T>
struct my_queue : std::queue<T, std::vector<T>>
{
  using std::queue<T, std::vector<T>>::queue;
  // in G++, the variable name is `c`, but it may or may not differ
  std::vector<T>& to_vector () { return this->c; }
};

That's it!!

Usage

my_queue<int> q;
q.push(1);
q.push(2);
std::vector<int>& v = q.to_vector();

Demo.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!