简体   繁体   中英

Define an iterator type in a stl container wrapper

What it the right way to write a wrapper class around an STL container, which is also a template (can accept a generic type T as element) and allows me to use an iterator as I would do with the STL container directly?

I want to do something of the following type

#include <list>
#include <iostream>

class MyClass{};

template<class T>
class Wrapper
{
    public:
        typename std::list<T>::iterator iterator;

        std::list<T> elements;
        iterator begin(){ return elements.begin(); };
        iterator end(){ return elements.end(); };
};

int main()
{
    Wrapper<MyClass> wrapper;

    for (Wrapper::iterator it = wrapper.begin(); it != wrapper.end(); ++it)
        std::cout<<"Hi"<<std::endl;
}

But the compiler says:

 error: ‘iterator’ in ‘class Wrapper<T>’ does not name a type

You have two errors.

Like Igor Tandetnik said in a comment, your iterator is a data member, not a nested type of your case.

You have to do this in your class:

typedef typename std::list<T>::iterator iterator;

or, in C++11:

using iterator = typename std::list<T>::iterator;

Also, you are using iterator the wrong way in your main() code. It should be like this:

Wrapper<MyClass>::iterator it = wrapper.begin()

Or, in C++ 11 or later, you can do this:

for(const auto &element : wrapper) {
    ...
}

Personally, I would prefer to use private inheritance instead of encapsulation. In my head, public inheritance means an "IS A" relationship, whereas private inheritance means an "IS IMPLEMENTED IN TERM OF" relationship.

You could do it like this:

template<typename T>
class WrapperList : private List<T> {
    ... Your code that belongs to your wrapper
}

If you want to wrap std::list and enrich it by some functionalities and, essentially, maintain some (or most) of the std::list interface (as the iteration capabilities) also in the wrapper, there are (at least) two possibilities.

One (composition) is to define begin(), end() methods and iterator type in the wrapper as to propagate them to the contained structure, as proposed by Andrea Araldo.

One (private inheritance) is to leverage private inheritance, which can be quite handy in some cases. Steps needed:

  • privately derive from std::list
  • implement the wrapper-specific methods, if any (what you need to add in the wrapper)
  • expose in the wrapper interface the methods and types of the std::list public interface, which you are interested in, through the 'using-declaration' ( http://en.cppreference.com/w/cpp/language/using_declaration ).

For example, in the following there is a slight modification of the proposed snippet in this sense (WrapperPrivInh). In this case, also all the standard constructors of std::list are made available in the wrapper interface, for free. Furthermore, for completeness, also WrapperCompos variation is proposed, where the list is contained into the wrapper, which exposes what is needed to support iterator-based loop. In this case, only the 'number of default elements' constructor is re-defined from scratch.

#include <list>
#include <iostream>

class MyClass
{ 
public:
    MyClass() { std::cout<<"(DEBUG)MyClass::default constructor\n"; }
};

template<class T>
class WrapperPrivInh : private std::list<T>
{
    public:
        using std::list<T>::iterator;  //for the iterator type
        using std::list<T>::begin;     // for the begin()
        using std::list<T>::end;       //  and end() methods
        using std::list<T>::list;      // for the constructors, if needed

        //std::list<T> elements;
        //iterator begin(){ return elements.begin(); };
        //iterator end(){ return elements.end(); };
};

template<class T>
class WrapperCompos 
{
    std::list<T> m_list;

    public:
        using iterator= typename std::list<T>::iterator;  //for the iterator type

        WrapperCompos(int const n) : m_list(n) { }

        iterator begin(){ return m_list.begin(); };
        iterator end(){ return m_list.end(); };
};


int main()
{
    {
        std::cout<<"Experiment with private inheritance"<<'\n';
        WrapperPrivInh<MyClass> wrapper(3);  // constructor witch builds N (3 here) MyClass default elements (derived "for free" from std::list)

        for(WrapperPrivInh<MyClass>::iterator it = wrapper.begin(); it != wrapper.end(); ++it)
            std::cout<<"Hi "<<&(*it)<<std::endl;
    }

    {
        std::cout<<"\nExperiment with private inheritance"<<'\n';
        WrapperCompos<MyClass> wrapper(3);  // constructor witch builds N (3 here) MyClass default elements (derived "for free" from std::list)

        for(WrapperCompos<MyClass>::iterator it = wrapper.begin(); it != wrapper.end(); ++it)
            std::cout<<"Hi "<<&(*it)<<std::endl;
        for( auto const& x : wrapper )  //range-for-loop syntax (from c++11)
            std::cout<<"Hi2 "<<&x<<std::endl;
    }
}

The private inheritance approach can be interesting to easily, and correctly, propagate the features of the wrapped structure to the wrapper. For instance the wrapper can be easily made compatible with std::algorithms if the wrapped structure is compatible with them, like std::list is. Hope this helps. Best

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