«Универсальный» итератор в c ++

Asked
Viewd5967

13

У меня есть:

 void add_all_msgs(std::deque<Message>::iterator &iter);
 

Как сделать эту функцию «универсальной», чтобы она могла принимать любые итераторы ввода? Меня действительно не волнует, повторяет ли он двухстороннюю очередь, вектор или что-то еще, пока итератор повторяет сообщение. - возможно ли это вообще в C ++?

7 ответов

8
 template<class InputIterator>
void add_all_msgs(InputIterator iter);
 

Использование:

 std::deque<Message> deq;
add_all_msgs(deq.begin());
 
  • Хех, или возвращаемое значение от "begin", если подумать. Приведенный выше код «использования» не компилируется: «недопустимая инициализация неконстантной ссылки типа« blah »из временного объекта типа« blah »».

    Steve Jessop09 июля 2009, 14:17
  • Это тоже можно сделать на C ++ 98. См. Мой ответ ниже или ответ с использованием BOOST_STATIC_ASSERT. Сообщение об ошибке не так хорошо, как то, которое вы получили бы при использовании концепций, но, по крайней мере, оно соответствует точке ошибки в стеке создания экземпляров.

    Paolo Capriotti09 июля 2009, 14:47
  • @Pauolo: Это будет охвачено Concepts в C ++ 0x, но пока нам просто нужно обойтись назначением или созданием сообщения из * iter, и компилятор сообщит нам, если он не может .

    Phil Miller09 июля 2009, 14:30
  • ОП явно сказал: «пока итератор повторяет сообщение». Это и большинство других решений полностью игнорировали это требование.

    Paolo Capriotti09 июля 2009, 14:26
  • По соглашению итераторы следует брать по значению, а не по ссылке. Помимо того, что итераторы в любом случае обычно «маленькие», причина этого (afaik) состоит в том, чтобы позволить вызывающей стороне передать временное значение, такое как возвращаемое значение из std :: back_inserter. C ++ 0x помогает в этом по крайней мере двумя способами, о которых я могу думать.

    Steve Jessop09 июля 2009, 14:07
1

Немного проще, чем вышеприведенное (в том, что он использует существующие библиотеки):

 #include <boost/static_assert.hpp> // or use C++0x static_assert
#include <boost/type_traits/is_same.hpp>

template <typename InputIterator>
void add_all_msgs( InputIterator it ) {
    BOOST_STATIC_ASSERT(( boost::is_same<
        typename std::iterator_traits<InputIterator>::value_type,
        Message>::value ));
    // ...
 
5

Если вы хотите, чтобы компилятор проверял, действительно ли итератор ссылается на объект Message, вы можете использовать следующий прием.

 template <typename InputIterator, typename ValueType>
struct AddAllMessages { };

template <typename InputIterator>
struct AddAllMessages<InputIterator, Message> {
  static void execute(const InputIterator &it) {
    // ...
  }
};

template <typename InputIterator>
void add_all_msgs(const InputIterator &it) {
  AddAllMessages<InputIterator, 
                 typename std::iterator_traits<InputIterator>::value_type>::execute(it);
}
 
2

Если вы не хотите использовать шаблон для функции add_all_msgs, вы можете использовать adobe :: any_iterator а>:

 typedef adobe::any_iterator<Message, std::input_iterator_tag> any_message_iterator;
void add_all_msgs(any_message_iterator begin, any_message_iterator end);
 
  • Версия шаблона, безусловно, более эффективна: с any_iterator у вас есть вызов виртуальной функции при каждой операции (т. е. переход к следующей позиции и разыменование) по сравнению с вызовом встроенной функции.

    Greg Rogers09 июля 2009, 16:30
  • Я пока буду создавать шаблоны add_all_msgs, хотя в идеале я хотел бы именно этого.

    nos09 июля 2009, 15:52
0
 #include <deque>
#include <vector>
#include <list>
#include <string>
using namespace std;

template<typename T>
void add_all_msgs(T &iter)
{

}

int _tmain(int argc, _TCHAR* argv[])
{
    std::deque<string>::iterator it1;
    std::vector<string>::iterator it2;
    std::list<string>::iterator it3;

    add_all_msgs(it1);
    add_all_msgs(it2);
    add_all_msgs(it3);


    return 0;
}
 
  • А add_all_msgs должен быть более-менее шаблоном? Итераторы не «полиморфны»?

    nos09 июля 2009, 14:10
  • @noselasd: итераторы не являются производными от общего базового класса, если вы это имеете в виду.

    Chris Jester-Young09 июля 2009, 14:11
  • Таким образом, они полиморфны (посредством статической утиной печати), но не в том смысле, в каком вы могли бы подумать о полиморфизме, скажем, в Java.

    Chris Jester-Young09 июля 2009, 14:12
1

С итераторами в стиле C ++ сложно добиться динамического полиморфизма. operator++(int) возвращается по значению, с которым трудно справиться: у вас не может быть виртуальной функции-члена, которая возвращает *this по значению без ее нарезки.

Если возможно, рекомендую использовать шаблоны, как говорят все.

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

 struct MessageIterator {
    virtual Message &get() = 0;
    virtual void next() = 0;
    // add more functions if you need more than a Forward Iterator.
    virtual ~MessageIterator() { };  // Not currently needed, but best be safe
};

// implementation elsewhere. Uses get() and next() instead of * and ++
void add_all_msgs(MessageIterator &it);

template <typename T>
struct Adaptor : public MessageIterator {
    typename T::iterator wrapped;
    Adaptor(typename T::iterator w) : wrapped(w) { }
    virtual Message &get() {
        return *wrapped;
    }
    virtual void next() {
        ++wrapped;
    }
};

int main() {
    std::deque<Message> v;
    Adaptor<std::deque<Message> > a(v.begin());
    add_all_msgs(a);
}
 

Я проверил, что это компилируется, но не тестировал и никогда раньше не использовал этот дизайн. Я также не стал беспокоиться о константе - на практике вам, вероятно, понадобится const Message &get() const. И на данный момент адаптер не знает, когда остановиться, но и код, с которого вы начали, тоже не знает, поэтому я проигнорировал это. слишком. По сути, вам понадобится функция hasNext, которая сравнивает wrapped с конечным итератором, предоставленным конструктору.

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

[Edit: если подумать, вероятно, лучше иметь шаблон функции-заглушки add_all_msgs, который оборачивает свой параметр в адаптер, а затем вызывает real_add_all_msgs. Это полностью скрывает адаптер от клиента.]

13
 template <typename Iterator>
void add_all_messages(Iterator first, Iterator last)
 

использование:

 vector<message> v;
add_all_messages(v.begin(), v.end());
 

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

  • Подождите, OP говорит, что это итератор ввода. Дело принято. :-P

    Chris Jester-Young09 июля 2009, 14:08
  • На самом деле я предполагал (в отличие от aJ), что итератор используется как итератор вывода, и в этом случае конечный итератор излишен. :-P

    Chris Jester-Young09 июля 2009, 14:07