简体   繁体   中英

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

I need to make use of a queue of doubles because of the good properties it has as an ordered container. I want to pass this queue to a class constructor that accepts vectors. If I do that directly I get the following error:

candidate constructor not viable: no known conversion from 'std::queue' to 'std::vector &' for 2nd argument

How to cast a queue to a vector?

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.

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 .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM