Как написать функцию на C ++, которая может возвращать итератор или reverse_iterator - PullRequest
5 голосов
/ 15 июля 2011

Насколько я могу судить, в c ++ нет общего базового класса, который бы охватывал итераторы и reverse_iterator.

Единственное предложение, которое я видел до сих пор, - это обойти это с помощью шаблонов ( Как написать функцию, которая использует итератор или коллекцию в общем виде? )

Однако, похоже, это решение не работает для меня.

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));
    }
};

В этом случае generate_iterator () не может использоваться по желанию, потому что компилятор жалуется, что «generate_iterator не является членом класса MyClass», вероятно, потому что я не указал его (что на практике я не могу, так как вызывающий должен быть независимым типа итератор).

Кто-нибудь может помочь? Заранее спасибо!

edit : как отметил Марк B, generate_iterator должен вернуть указатель - теперь исправлено

обновление : только что начал использовать это http://thbecker.net/free_software_utilities/type_erasure_for_cpp_iterators/start_page.html и похоже, что оно работает ...

Ответы [ 4 ]

3 голосов
/ 15 июля 2011

Вы можете создать свой собственный класс итератора, который знает, как идти в обоих направлениях.Инкапсулируйте оба типа итераторов и внутренне выберите тот, с которым вы были инициализированы.

Вот начало:

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;
};
2 голосов
/ 15 июля 2011

В C ++ вы не можете написать функцию, которая возвращает два разных типа. В случае с вашим шаблоном он будет возвращать один или другой в зависимости от экземпляра. Вы могли бы вернуть базовый указатель на полиморфный итератор, но это заставило бы меня спросить, что вы действительно пытаетесь сделать здесь. Даже стандартные контейнеры не пытаются это сделать: у них есть begin и rbegin, чтобы различать должным образом. Я предложил бы иметь две отдельные функции, каждая из которых делает правильные вещи и возвращает один тип итератора или другой, как того требует контекст.

Обратите внимание, что вы не можете неявно определить создание экземпляра шаблона для типа, который используется только для возвращаемого типа функции.

1 голос
/ 15 июля 2011

С помощью буст-кортежа и буста любого ваша проблема может быть легко решена. Я написал пример, используя boost :: any, см. Ниже:

#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;
    }
}
0 голосов
/ 15 июля 2011

Используйте boost :: option или 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(...)
  ...;

Хотя к variant лучше обращаться через посетителя.

Недостатком является то, что вам нужна какая-то плита котла для извлечения нужного типа, и это именно та причина, по которой что-то вроде any_iterator может быть более разумным выбором.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...