Is there a way to access the underlying container of STL container adaptors?

梦想与她 提交于 2019-11-26 15:23:18

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

jxh

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> > * without type punning. 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 liberal use of typedefs).

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.

This SGI page is the most "official" documentation available online, I believe.

The reason that you can't get direct access to the underlying container is that the adapter changes the usage pattern, and having the methods of the underlying container available would violate that new usage pattern. For example:

2 This restriction is the only reason for queue to exist at all. Any container that is both a front insertion sequence and a back insertion sequence can be used as a queue; deque, for example, has member functions front, back, push_front, push_back, pop_front, and pop_back 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. http://www.sgi.com/tech/stl/queue.html

If you want to get around this design feature, you can do something like:

template <typename T>
class clearable_queue : public std::queue<T>
{
public:
    void clear() { c.clear(); }
};

As a general rule, any identifier that starts with an underscore is a vendor-specific extension or an implementation detail. So _Get_container() is just an addition made by Microsoft because it simplified their implementation. It is not intended to be used.

As for where to find the documentation, it's split into several parts.

The authoritative source is, of course, the language standard. As Neil Butterworth said, there are draft copies available for free online (which are still very useful. The differences from those to the final version are really minimal). Alternatively, you can buy a copy. It should be available from whichever organization represents ISO in your country (and probably from a bajillion other sources as well). The document you're looking for is ISO/IEC 14882:2003 Programming Language C++. (14882 is the standard number. 2003 is the year of the last revision. If you come across the 1998 version, you can use that too. The differences are really ridiculously small between the two, and basically just amounts to a few clarifications. It's probably best to stay away from the drafts for C++0x, as the changes there are far more extensive)

Apart from that, every implementation of the standard library is required to document a large number of details (implementation-defined behavior, things which are not specified in the standard, but is left up to the library implementer). And in addition, most of them also put up a detailed documentation of the entire library as well.

Microsoft has detailed documentation available on MSDN. The documentation respects the standard, and clearly marks all nonstandard extensions so you know which is which.

SGI also has the documentation online (although it is older and in some cases, not entirely accurate).

IBM has similar documentation available on their website, and I believe GCC does too.

I certainly hope there isn't a way to access the underlying container of a priority queue. If you could then you could mess up the internal heap structure of the priority queue. In any case the point of those adaptors is that they only present you with the minimal interface of the stack or queue, and abstract out all the other things. So if you needed to use any other features, you should have used the original container directly.

As for documentation of the STL, you can look at the documentation of SGI's STL here. There are a few differences between SGI's STL and the one in the C++ standard, but they are mostly noted on that site. Also, cppreference.com is a wiki documentation of the C++ library that is becoming more complete.

You can write a subclass to retrieve the member variable c. See this comment from libstdc++.

protected:
/**
 *  'c' is the underlying container.  Maintainers wondering why
 *  this isn't uglified as per style guidelines should note that
 *  this name is specified in the standard, [23.2.3.1].  (Why?
 *  Presumably for the same reason that it's protected instead
 *  of private: to allow derivation.  But none of the other
 *  containers allow for derivation.  Odd.)
 */
_Sequence c;

where can I find official documentation of the standard library?

The C++ standard is available in hardcover, ISBN 0470846747. Drafts of Standard are widely available in PDF form, but you'll have to be careful to match them with the official versions (C++98,03,11 or 14). The current drafts exceed the C++14 standard.

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