简体   繁体   中英

How to write a c++ function what can return either iterator or reverse_iterator

As far as I can tell in c++ there is no common base class that covers both iterator and reverse_iterator.

The only suggestion I have seen so far is to get around this using templates ( How to write a function that takes an iterator or collection in a generic way? )

However this solution doesn't seem to work for me.

class MyClass
{
    template<typename Iter> Iter* generate_iterator(...params...)
    {
        //returns either a vector::iterator or vector::reverse_iterator
    }
    template<typename Iter> void do_stuff(Iter *begin, Iter *end)
    {
        //does stuff between elements specified by begin and end
        //I would like this function to remain agnostic of which direction it is working in!
    }
    void caller()
    {
        //I would like this function to remain agnostic of which direction it is working in too...
           do_stuff(generate_iterator(blah),generate_iterator(foo));
    }
};

In this case, generate_iterator() cannot be used as desired because the compiler complains "generate_iterator is not a member of class MyClass" presumably because I haven't specified it (which I can't in practice as caller should be agnostic of the iterator type).

Can anyone help? Thanks in advance!

edit : as Mark B pointed out generate_iterator must return a pointer - now corrected

update : just started using this http://thbecker.net/free_software_utilities/type_erasure_for_cpp_iterators/start_page.html and it seems to work...

You can create your own iterator class that knows how to go both directions. Encapsulate both types of iterator and internally select whichever one you were initialized with.

Here's a start:

template<typename Container>
class BiIterator
{
public:
    BiIterator(Container::iterator i) : m_fwd(i), m_isforward(true) {}
    BiIterator(Container::reverse_iterator i) : m_rev(i), m_isforward(false) {}
    bool operator==(const BiIterator & left, const BiIterator & right);
    Container::value_type & operator*()
    {
        if (m_isforward)
            return *m_fwd;
        return *m_rev;
    }
    const Container::value_type & operator*() const;
    BiIterator & operator++()
    {
        if (m_isforward)
            ++m_fwd;
        else
            ++m_rev;
        return *this;
    }
private:
    Container::iterator         m_fwd;
    Container::reverse_iterator m_rev;
    bool                        m_isforward;
};

In C++ you can't write a function that returns two different types. In your template case it will return one or the other depending on the instantiation. You could possibly return a base pointer to a polymorphic iterator but that would cause me to ask what you're really trying to do here. Even the standard containers don't try to do that: They have begin and rbegin to distinguish properly. I would suggest having two separate functions that each do the right thing and return one type of iterator or the other as context dictates.

As a side, note that you can't implicitly determine a template instantiation of a type that's only used for the return type of a function.

By using boost tuple and boost any, your problem can be easily solved. I wrote a example by using boost::any, see below:

#include <boost/any.hpp>
using boost::any_cast;
#define MSG(msg) cout << msg << endl;
boost::any getIterator(std::vector<int>& vec, bool bReverse)
{
    if(!bReverse)
        return boost::any(vec.begin());
    else
        return boost::any(vec.rbegin());
}

int main() 
{
    std::vector<int> myvec;
    myvec.push_back(1);
    myvec.push_back(2);
    myvec.push_back(3);


    typedef std::vector<int>::iterator vecIter;
    typedef std::vector<int>::reverse_iterator vecRIter;
    try
    {
        boost::any iter  = getIterator(myvec, false);
        boost::any iter2 = getIterator(myvec, true);
        vecIter  it1 = any_cast<vecIter>(iter);
        vecRIter it2 = any_cast<vecRIter>(iter2);
        MSG(*it1);//output 1
        MSG(*it2);//output 3
        return true;
    }
    catch(const boost::bad_any_cast &)
    {
        return false;
    }
}

Use boost::variant or boost::any .

boost::variant< reverse_iterator, iterator >
generate_iterator(...) {
  if(...) return iterator();
  else return reverse_iterator();
}

// user code
boost::variant< reverse_iterator, iterator > v = generate_iterator();

if(reverse_iterator* it = boost::get<reverse_iterator>(v))
  ...;
else if(...)
  ...;

Although the variant is better accessed through a visitor.

The downside is that you need some boiler plate to extract the proper type and is exactly the reason why something like any_iterator might be a more sensible choice.

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