"end ()" iterator for insertions?

For iterators, such as those that come back from std::back_inserter(), is there something that can be used as an "end" iterator?

At first this seems a little pointless, but I have an API that:

template<typename InputIterator, typename OutputIterator>
void foo(
    InputIterator input_begin,
    InputIterator input_end,
    OutputIterator output_begin,
    OutputIterator output_end
);

fooperforms some operation on the input sequence, generating the output sequence. (The length of which is known foo, but may or may not be equal to the length of the input sequence.)

Accepting a parameter output_endis odd: std::copyit does not, for example, and assumes that you are not going to pass this garbage. foodoes this to check the range: if you pass the range too small, it throws an exception in the name of defensive programming. (Instead of potentially overwriting random bits in memory.)

Now, let's say I want to pass fooback inserters, in particular, one of std::vectorwhich has no limits beyond the limits of memory. I still need the "end" iterator - in this case something that can never be compared to an equal. (Or, if I had std::vector, but with a length limit, perhaps he would sometimes compare equal?)

? API foo - ? ( , .) , "" () .

+5
3

foo , distance(output_begin, output_end) , , "" ? A back_inserter ; distance , a back_inserter , , 0.

A foo std::copy - foo(InIt, InIt, OutIt) - , , . "". , (, Visual ++) .

back_inserting_foo(InIt, InIt, Container), , , .

+5

API foo(), , , curr_output_iter != output_end, (. ), distance() .

" ", "" , , . OutputIterator SafeOutputIterator - , , ++. "" "" : , 2- , .

/* 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 (-, , STL). soi_begin_cont() soi_end_cont() .

, STL - , , , .

+2

j_random_hacker, , , , ,

template <typename InIt1, InIt2, OutIt1, OutIt2>
void foo(InIt1 in_begin, InIt2 in_end, OutIt1 out_begin, OutIt2 out_end) { }

back_inserter_end :

class back_inserter_end 
    : std::iterator<std::output_iterator_tag, void>
{ };

bool operator==(back_inserter_end, back_inserter_end) { return true;  }
bool operator!=(back_inserter_end, back_inserter_end) { return false; }

template <typename Container>
bool operator==(const back_insert_iterator<Container>&, back_inserter_end) { 
     return false; 
}

template <typename Container>
bool operator==(back_inserter_end, const back_insert_iterator<Container>&) { 
    return false; 
}

template <typename Container>
bool operator!=(const back_insert_iterator<Container>&, back_inserter_end) { 
    return true; 
}

template <typename Container>
bool operator!=(back_inserter_end, const back_insert_iterator<Container>&) { 
    return true; 
}

"" :

foo(it, it, std::back_inserter(v), back_inserter_end());

Inside your "safe" algorithm, you can check if there are any out_it == out_endbefore using out_it; as it out_itis back_insert_iterator, this test will always return false(which is the desired behavior).

+1
source

All Articles