How can I expose iterators without exposing the container used?

前端 未结 4 1123
野性不改
野性不改 2020-12-24 08:14

I have been using C# for a while now, and going back to C++ is a headache. I am trying to get some of my practices from C# with me to C++, but I am finding some resistance a

相关标签:
4条回答
  • 2020-12-24 08:49

    You may find the following article interesting as it addresses exactly the problem you have posted: On the Tension Between Object-Oriented and Generic Programming in C++ and What Type Erasure Can Do About It

    0 讨论(0)
  • 2020-12-24 08:55

    I am unsure about what you mean by "not exposing std::vector publicly" but indeed, you can just define your typedef like that:

    typedef typename std::vector<T>::iterator iterator;
    typedef typename std::vector<T>::const_iterator const_iterator; // To work with constant references
    

    You will be able to change these typedefs later without the user noticing anything ...

    By the way, it is considered good practice to also expose a few other types if you want your class to behave as a container:

    typedef typename std::vector<T>::size_type size_type;
    typedef typename std::vector<T>::difference_type difference_type;
    typedef typename std::vector<T>::pointer pointer;
    typedef typename std::vector<T>::reference reference;
    

    And if needed by your class:

     typedef typename std::vector<T>::const_pointer const_pointer;
     typedef typename std::vector<T>::const_reference const_reference;
    

    You'll find the meaning of all these typedef's here: STL documentation on vectors

    Edit: Added the typename as suggested in the comments

    0 讨论(0)
  • 2020-12-24 09:09

    I have done the following before so that I got an iterator that was independent of the container. This may have been overkill since I could also have used an API where the caller passes in a vector<T*>& that should be populated with all the elements and then the caller can just iterate from the vector directly.

    template <class T>
    class IterImpl
    {
    public:
        virtual T* next() = 0;
    };
    
    template <class T>
    class Iter
    {
    public:
        Iter( IterImpl<T>* pImpl ):mpImpl(pImpl) {};
        Iter( Iter<T>& rIter ):mpImpl(pImpl) 
        {
            rIter.mpImpl = 0; // take ownership
        }
        ~Iter() {
            delete mpImpl; // does nothing if it is 0
        }
        T* next() {
        return mpImpl->next(); 
        }
    private:
        IterImpl<T>* mpImpl; 
    };
    
    template <class C, class T>
    class IterImplStl : public IterImpl<T>
    {
    public:
        IterImplStl( C& rC )
        :mrC( rC ),
        curr( rC.begin() )
        {}
        virtual T* next()
        {
        if ( curr == mrC.end() ) return 0;
        typename T* pResult = &*curr;
        ++curr;
        return pResult;
        }
    private:
        C& mrC;
        typename C::iterator curr;
    };
    
    
    class Widget;
    
    // in the base clase we do not need to include widget
    class TestBase
    {
    public:
        virtual Iter<Widget> getIter() = 0;
    };
    
    
    #include <vector>
    
    class Widget
    {
    public:
        int px;
        int py;
    };
    
    class Test : public TestBase
    {
    public:
        typedef std::vector<Widget> WidgetVec;
    
        virtual Iter<Widget> getIter() {
            return Iter<Widget>( new IterImplStl<WidgetVec, Widget>( mVec ) ); 
            }
    
        void add( int px, int py )
        {
            mVec.push_back( Widget() );
            mVec.back().px = px;
            mVec.back().py = py;
        }
    private:
        WidgetVec mVec;
    };
    
    
    void testFn()
    {
        Test t;
        t.add( 3, 4 );
        t.add( 2, 5 );
    
        TestBase* tB = &t;
        Iter<Widget> iter = tB->getIter();
        Widget* pW;
        while ( pW = iter.next() )
        {
            std::cout << "px: " << pW->px << " py: " << pW->py << std::endl;
        }
    }
    
    0 讨论(0)
  • 2020-12-24 09:10

    This should do what you want:

    typedef typename std::vector<T>::iterator MyIterator;
    

    From Accelerated C++:

    Whenever you have a type, such as vector<T>, that depends on a template parameter, and you want to use a member of that type, such as size_type, that is itself a type, you must precede the entire name by typename to let the implementation know to treat the name as a type.

    0 讨论(0)
提交回复
热议问题