Is there a way to access the underlying container of STL container adaptors?
I spotted the following solution somewhere on the web and I'm using it in my projects:
template <class T, class S, class C>
S& Container(priority_queue<T, S, C>& q) {
struct HackedQueue : private priority_queue<T, S, C> {
static S& Container(priority_queue<T, S, C>& q) {
return q.*&HackedQueue::c;
}
};
return HackedQueue::Container(q);
}
int main()
{
priority_queue<SomeClass> pq;
vector<SomeClass> &tasks = Container(pq);
return 0;
}
Have fun :).
C++ : STL Container adapters
When to initialize to the copy of a container and when to use an underlying container?
Both of these...
std::queue<int> second(mydeck);
std::queue<int,std::list<int> > fourth(mylist);
...construct and initialise a queue by copying elements from the container specified as constructor argument (i.e. mydeck
and mylist
respectively).
If you mean to ask why the second specifies a second template argument of std::list<int>
, that's because std::queue
can store the data in any container that provides the API functions it expects. From cppreference, that second template parameter is:
Container - The type of the underlying container to use to store the elements. The container must satisfy the requirements of
SequenceContainer
. Additionally, it must provide the following functions with the usual semantics:
back()
front()
push_back()
pop_front()
The standard containers std::deque and std::list satisfy these requirements.
Of these, I'd guess that std::list
would typically be more efficient for one or a very few elements (exactly where the cutoff is depends on object size, memory library performance characteristics, CPU cache sizes, system load etc. - it gets complicated), then std::deque
will have better average performance and memory usage for larger numbers of elements (with fewer but larger dynamic memory allocations/deallocations). But even an educated guess can go badly wrong for some specific use cases - if you care enough to consider tuning this, you should measure performance with each candidate container, and your actual data and usage, to inform your decision. Having the container be a template parameter allows the programmer to choose what's best for their needs.
The parameter also has a default...
template <class T, class Container = std::deque<T>> class queue;
...so you only need to explicit specify a container if you're not happy to have it use a std::deque
.
How to change the fundamental underlying container type for the adaptor containers?
Give the required container as templatized parameter. For example:
std::stack<int,std::vector<int> > stack_using_vector_of_int;
std::stack<std::string,std::list<std::string> > stack_using_list_of_string;
If you want to make sure stack
always uses list, give a type definition of list_stack
that always uses std::list
as container.
Adding from Jarod42's comment:
Post C++11 you can use
template <typename T>
using list_stack = std::stack<T, std::list<T>>;
By default std::stack
uses std::deque
as underlying container which is almost always more efficient than using std::list
or std::vector
.
What are Containers/Adapters? C++
<joke>
C++ is technical and hard to understand :-D</joke>
Containers are data types from STL that can contain data.
Example: vector
as a dynamic array
Adapters are data types from STL that adapt a container to provide specific interface.
Example: stack
providing stack interface on top of the chosen container
(side note: both are actually templates not data types, but the definition looks better this way)
Can I use member function of underlying container of priority_queue
The container adapters are designed specifically to abstract away the underlying container. It provides a new abstraction on its own.
So, no, you cannot call members of the vector inside the queue. What you can do however, is call functions on the vector before it enters the queue. And then move it in place:
#include<iostream>
#include<queue>
#include<vector>
using namespace std;
int main(){
vector<int> v;
v.reserve(25);
v.push_back(1);
priority_queue<int , vector<int>, greater<int>> p(greater<int>(), std::move(v));
return 0;
}
Still no way to output the contents of a queue or stack, as you can only access the top()
element (on purpose, as part of the abstraction).
Why don't the standard C++ container adaptors provide a clear function?
Well, I think this is because clear
was not considered a valid operation on a queue, a priority_queue or a stack (by the way, deque is not and adaptor but a container).
The only reason to use the container
adaptor queue instead of the container
deque is to make it clear that you are
performing only queue operations, and
no other operations. (from the sgi page on queue)
So when using a queue, all you can do is push/pop elements; clearing the queue can be seen as a violation of the FIFO concept. Consequently, if you need to clear your queue, maybe it's not really a queue and you should better use a deque.
However, this conception of things is a little narrow-minded, and I think clearing the queue as you do is fair enough.
Iterating vector-based priority_queue
In short, yes.
The standard [priqueue.members] defines the operators on a priority queue in terms of push/emplace/pop_back and heap operations. It is trivial to see that the size of the underlying container will be equal to the size of the priority queue, and that elements will be stored starting from the beginning of the container.
In fact, the standard even guarantees that the underlying container will be a heap as defined by the standard make_heap
algorithm.
That said, it is likely worth considering whether or not priority_queue
is the best solution for your problem if iteration is required.
Why don't the standard C++ container adaptors provide a clear function?
Well, I think this is because clear
was not considered a valid operation on a queue, a priority_queue or a stack (by the way, deque is not and adaptor but a container).
The only reason to use the container
adaptor queue instead of the container
deque is to make it clear that you are
performing only queue operations, and
no other operations. (from the sgi page on queue)
So when using a queue, all you can do is push/pop elements; clearing the queue can be seen as a violation of the FIFO concept. Consequently, if you need to clear your queue, maybe it's not really a queue and you should better use a deque.
However, this conception of things is a little narrow-minded, and I think clearing the queue as you do is fair enough.
Difference between sequence containers and container adaptors in c++
Sequence containers
You could see sequence container as containers "built from scratch". They use different structures to hold data and have different algorithmic time to insert, delete and retreive element.
You can find a lot of information about the algorithmic time of containers here
Container adapters
Container adapters are behavior added over sequence containers making them respect different paradigms. The added behavior can be a stricter behavior (the stack will only allow you to pop/push item on it, no random insert). They are other type of containers that did not need a new storing behavior then those already existing. As an example, a stack could be built over a vector. It would then uses the data structure of vector, but restrain the usage to a certain set of functions to mimic a stack.
Most important over all this is to make sure that you use the right container to suit your needs. A stricter container will help you prevent missuses of your datum and knowing the usage of your data will help you chose the good container to get the best performances.
More information about container adapters can be found here
What should I use most of the time?
Many experts (Scott Meyer, Bjarne Stroustrup) suggest the use of the vector
by default while others (like Herb Sutter as Steve Jessop pointed out) suggest the deque
. I would strongly suggest you to chose the container that best suits your need.
Related Topics
Is There a Compiler Hint for Gcc to Force Branch Prediction to Always Go a Certain Way
Extract Year/Month/Day etc. from Std::Chrono::Time_Point in C++
Any Reason to Overload Global New and Delete
Generate Sha256 with Openssl and C++
How to Convert Euler Angles to Directional Vector
Algorithm for Finding the Smallest Power of Two That's Greater or Equal to a Given Value
Why Do I Need to Use Typedef Typename in G++ But Not VS
Using C++ Filestreams (Fstream), How to Determine the Size of a File
Are Function Static Variables Thread-Safe in Gcc
Send Mail Using Smtp in C++ on Linux
C++ Portability Between Windows and Linux
Detecting If Computer Is Idle Based on Mouse and Keyboard Interactions
Mpirun: Unrecognized Argument Mca
Way Cross Compile C/C++ Code to Run on Windows, Linux, and MAC Os
External Library Throws Undefined Reference Errors in Qt Creator