С итераторами в стиле 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
. Это полностью скрывает адаптер от клиента.]
Хех, или возвращаемое значение от "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