A container adaptor is not a container at all, since it doesn't implement the interface of a container (doesn't provide an iterator, etc). Instead, it provides limited access to a container. Those provided in the standard library operate using a sequence.
Explanation: Container Adaptors is the subset of Containers that provides a different interface for sequential containers.
@BjörnPollex Yes! I forgot to mention that.
Container Adapter These types of containers are called container adapters. The C++ Standard Library implements class templates such as stack, queue, and priority_queue as a container that puts constraints on the process of storage and retrieval of elements.
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 :).
Based on the accepted answer, a more general approach:
template <class ADAPTER>
typename ADAPTER::container_type & get_container (ADAPTER &a)
{
struct hack : ADAPTER {
static typename ADAPTER::container_type & get (ADAPTER &a) {
return a.*&hack::c;
}
};
return hack::get(a);
}
As I learned from this answer, .*&
is actually two operators, where the pointer resulting from &hack::c
(which has type ADAPTER::container_type ADAPTER::*
) is the target or the .*
operator to retrieve the underlying container itself. hack
has access to the protected member, but after the pointer is obtained, protections are lost. So a.*(&hack::c)
is allowed.
I mentioned it in a comment, but after some thinking, it seems to be an OK solution. queue
/stack
/priority_queue
(that is, all of the adapter classes) all have a protected
member c
which is the underlying container (see ISO/IEC 14882:2003 section 23.2.2.4), so if you inherit from any of these, you can access it directly.
I know the typical wisdom is to not inherit from STL containers due to non-virtual dtors, but this case is an exception. The goal is not to overload functionality, but to make minor extensions to the interface of the adapter. Here is an example of adding the ability to access the underlying container.
#include <queue>
#include <iostream>
template <class Container>
class Adapter : public Container {
public:
typedef typename Container::container_type container_type;
container_type &get_container() { return this->c; }
};
int main() {
typedef std::queue<int> C;
typedef Adapter<C> Container;
Container adapter;
for(int i = 0; i < 10; ++i) {
adapter.push(i);
}
Container::container_type &c = adapter.get_container();
for(Container::container_type::iterator it = c.begin(); it != c.end(); ++it) {
std::cout << *it << std::endl;
}
}
Unfortunately, you'll have to resort to type-punning to "upgrade" an existing std::queue<int> *
to a Adapter<std::queue<int> > *
. Technically, this would likely work fine... but I recommend against it:
typedef std::stack<int> C;
typedef Adapter<C> Container;
C stack;
// put stuff in stack
Container *adapter = reinterpret_cast<Container *>(&stack);
Container::container_type &c = adapter->get_container();
// from here, same as above
So I would recommend using typedefs to make it easy to swap between the two. (Also notice in my example that you only need to change 1 line to change it from a queue
to a stack
because of the liberal use of typedef
s).
No, there is no standard way of doing that. As for access to the standard, it is not available onn the web, you have to buy a copy! However, there are various copies of drafts available here.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With