Вы можете избежать изменения API foo()
, выполняя проверку безопасности на ходу, проверяя, что curr_output_iter != output_end
перед записью каждого элемента (см. Ниже), а не один раз при запуске с distance()
отметьте, как Джеймс МакНеллис предлагает .
Для этого потребуется написать собственный «адаптер итератора», чтобы обеспечить «расширенный» выходной итератор, который может проверить, находится ли он в конце своего допустимого диапазона. Тогда вы по праву изменили бы типовые имена шаблонов с OutputIterator
на SafeOutputIterator
- даже если это всего лишь документация, потому что это не может быть применено в C ++. Мы различаем «ограниченные» и «неограниченные» пары итераторов: для последних два итератора никогда не сравнятся равными, и фактически второй итератор никогда не понадобится ни для чего, кроме его типа.
/* Metafunction for determining whether the range has a fixed endpoint.
* Assume all iterators are bounded except for OutputIterators. */
template <typename Tag>
struct helper {
static bool value = true;
};
template <>
struct helper<output_iterator_tag> {
static bool value = false;
};
template <typename It>
struct is_bounded {
static bool value = helper<typename iterator_traits<It>::iterator_category>::value;
};
/* Wraps an output iterator. */
template<typename It, bool BOUNDED = is_bounded<It>::value>
class safe_output_iterator {
typedef typename iterator_traits<It>::value_type value_type;
public:
explicit safe_output_iterator(It iter, size_t limit = 0) : iter_(iter), limit_(limit) {}
safe_output_iterator& operator++() { /* Preinc */
++iter_;
return *this;
}
safe_output_iterator operator++(int) { /* Postinc */
safe_output_iterator temp(*this);
++iter_;
return temp;
}
/* Trick: Deferencing returns the same object, so that operator=() works */
safe_output_iterator& operator*() {
return *this;
}
/* Will be called despite "dereferencing" this iterator object */
safe_output_iterator& operator=() {
/* Insert check for limit == 0 here if you want */
--limit_;
return *_iter;
}
/* Addition to the OutputIterator concept. */
bool operator==(safe_output_iterator const& x) {
return BOUNDED && limit_ == x.limit_;
}
bool operator!=(safe_output_iterator const& x) {
return !(*this == x);
}
private:
It iter_;
size_t limit_;
};
/* Helper function templates to avoid typing out long typenames */
/* Handle iterators */
template <typename It>
safe_output_iterator<It> soi_begin(It it, size_t limit = 0) {
return safe_output_iterator<It>(it, limit);
}
template <typename It>
safe_output_iterator<It> soi_end(It it, size_t limit = 0) {
return safe_output_iterator<It>(it, limit);
}
/* Handle STL containers like vector and list */
template <typename C>
safe_output_iterator<It> soi_begin_cont(C cont) {
return safe_output_iterator<typename C::iterator>(cont.begin(), cont.size());
}
template <typename C>
safe_output_iterator<It> soi_end_cont(C cont) {
/* Actually the iterator supplied (here cont.end()) is unimportant... */
return safe_output_iterator<typename C::iterator>(cont.end());
}
Теперь вы можете написать код, например:
vector<int> u(10, 42), v(ENOUGH_SPACE), w, x(ENOUGH_SPACE);
// Explicit iterator pair; bounded
foo(u.begin(), u.end(), soi_begin(v.begin(), ENOUGH_SPACE), soi_end(v));
// Explicit iterator pair; unbounded (2nd iterator ignored, but we need the type)
foo(u.begin(), u.end(), soi_begin(w.back_inserter()), soi_end(w.back_inserter()));
// Use whole container
foo(u.begin(), u.end(), soi_begin_cont(x), soi_end_cont(x));
Вы можете либо foo()
проверить curr_output_iter != output_end
перед каждой записью через *curr_output_iter
, либо в качестве альтернативы вставить чек в safe_output_iterator::operator=()
. Последнее кажется предпочтительным, поскольку вы не можете забыть выполнить проверку всякий раз, когда пара safe_output_iterator
s передается любому произвольному алгоритму (предположительно, это похоже на то, как работают отладочные версии STL). Вы также можете написать перегрузки soi_begin_cont()
и soi_end_cont()
для массивов фиксированного размера.
Все это было бы гораздо менее громоздким, если бы диапазоны использовались алгоритмами STL вместо пар итераторов - таким образом, один шаблон функции мог бы возвращать соответствующий диапазон, охватывающий, например, весь массив.