“end()”后插入器的迭代器?

时间:2011-01-01 07:50:48

标签: c++ stl iterator

对于从std::back_inserter()返回的迭代器,有什么东西可以用作“结束”迭代器吗?

起初这似乎有点荒谬,但我有一个API:

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

foo对输入序列执行一些操作,生成输出序列。 (foo知道谁的长度,但可能与输入序列的长度相等或不同。)

获取output_end参数是奇怪的部分:例如,std::copy不执行此操作,并假设您不会将其传递给垃圾。 foo用它来提供范围检查:如果你传递的范围太小,它会以防御性编程的名义抛出一个异常。 (而不是潜在地覆盖内存中的随机位。)

现在,我想要传递foo一个后插件,特别是来自std::vector的插件,它没有内存限制之外的限制。我仍然需要一个“结束”迭代器 - 在这种情况下,一些永远不会比较平等的东西。 (或者,如果我有一个std::vector但是对长度有限制,或许有时可能比较相等?)

我该怎么做呢?我确实能够更改foo的API - 最好不检查范围,而是提供另一种方法来获得所需的输出范围? (无论如何对于原始数组都是必需的,但是后插入器不需要进入矢量。)这看起来不太稳健,但我正在努力使“强大”(上面)工作。

3 个答案:

答案 0 :(得分:5)

如果foo正在检查以确保distance(output_begin, output_end)足够大以包含结果,那么您可以将哪些用作“结束”迭代器? back_inserter将元素添加到结尾;根据定义,distance添加元素的位置与序列末尾之间的back_inserter0

我认为foostd::copy类似的签名foo(InIt, InIt, OutIt)是您的最佳选择。它实际上并不“不健全”。对于大多数算法,出于性能原因,您只想在调试版本中进行此类范围检查,并且一个像样的标准库实现(如Visual C ++标准库)已经在调试版本中提供了大量的范围检查。

或者,你可以创建一个back_inserting_foo(InIt, InIt, Container),虽然为此创建一个特殊情况会有点不寻常,并且会给函数用户带来更大的负担,以便知道他们需要为不同类型的迭代器使用哪个重载

答案 1 :(得分:3)

您可以通过执行安全检查来避免更改foo()的API,方法是在每个元素写出之前检查curr_output_iter != output_end(参见下文),而不是在开始时检查distance() OutputIterator检查James McNellis suggests

这样做需要编写自己的“迭代器适配器”来提供“增强型”输出迭代器,可以测试它是否在有效范围的末尾。然后你可以正确地将模板类型名从SafeOutputIterator更改为/* 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()); } - 即使这只是作为文档,因为它是在C ++中无法实现的。我们区分“有界”和“无界”迭代器对:对于后者,没有两个迭代器会比较相等,实际上第二个迭代器除了它的类型之外永远不需要它。

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中插入支票。后者似乎更好,因为你不能忘记每当一对soi_begin_cont()传递给任意算法时执行检查(可能这与STL的调试版本的工作方式类似)。您还可以为固定大小的数组编写soi_end_cont()和{{1}}的重载。

如果STL算法使用ranges而不是迭代器对,那么所有这些都不会那么麻烦 - 例如,单个函数模板可以返回跨越整个数组的适当范围。

答案 2 :(得分:1)

正如我在对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());

在“安全”算法中,您可以在使用out_it == out_end之前测试out_it;由于out_itback_insert_iterator,因此此测试将始终返回false(这是所需行为)。