简体   繁体   English

iterator和back_insert_iterator有什么区别?

[英]What's the difference between iterator and back_insert_iterator?

如果随机访问迭代器可以用于访问相对于它们指向的元素的任意偏移位置的元素(不知何故像指针),为什么不能在std::copy()等通用算法中使用它们而不是使用back_insert_iterator ,两者之间有什么区别?

std::back_insert_iterator is a specific kind of output iterator which supports push_back operation. std::back_insert_iterator是一种特定输出迭代器,它支持push_back操作。 When you write to it using operator= , it push_backs the value into the underlying container — so, in that sense it acts as an adaptor for containers which has push_back member function. 当您使用operator= 写入它时,它会将值push_backs到底层容器中 - 因此,从这个意义上说,它充当具有push_back成员函数的容器的适配器。

An example would be easy to understand: 一个例子很容易理解:

std::vector<int> v;

std::back_insert_iterator<std::vector<int>>  it(v);

*it = 10; // it is equivalent to v.push_back(10);
 it = 99; // it is ALSO equivalent to v.push_back(99);

for (auto const & i : v)
    std::cout << i << " " ; //10 99

It outputs : 它输出:

10 99

Online Demo . 在线演示

The usual iterator operations ++ and * on it has no effect. 通常的迭代器操作++* it没有影响。

But you rarely use them directly (I've never used it directly untill now). 但是你很少直接使用它们(我从未直接使用它直到现在)。 You use them with algorithms, such as std::copy in which case you also use std::back_inserter function which returns an object of type std::back_insert_iterator . 您可以将它们与算法一起使用,例如std::copy在这种情况下,您还可以使用std::back_inserter 函数返回std::back_insert_iterator类型的对象。

//assuming dest is a container which supports push_back!
std::copy(src.begin(), src.end(), std::back_inserter(dest));

You would also like to see the following (adaptor) iterators: 您还希望看到以下(适配器)迭代器:

So depending on the container, you choose adaptor iterator. 因此,根据容器,您选择适配器迭代器。

Note that all of them are output iterator. 请注意,它们都是输出迭代器。

why can't they be used in generic algorithms like std::copy() instead of using back_insert_iterator. 为什么不能在std :: copy()等通用算法中使用它们而不是使用back_insert_iterator。

Of course, you can use the random access iterators (or any output iterator) in algorithms like std::copy , as third argument, but that assumes the iterator is referencing to existing range — *it and ++it are well-defined for the value you passed. 当然,您可以在std::copy等算法中使用随机访问迭代器(或任何输出迭代器)作为第三个参数,但是假设迭代器引用现有范围 - *it++it是明确定义的你传递的价值。 You pass them to overwrite the existing elements of the range, whereas std::back_insert_iterator adds new elements to the container. 您传递它们以覆盖范围的现有元素,而std::back_insert_iterator 新元素添加到容器中。

Hope that helps. 希望有所帮助。

Actually, you can totally use a regular iterator in std::copy . 实际上,你可以在std::copy完全使用常规迭代器。

int main() {
    std::vector<int> vec{1, 2, 3, 4};

    std::list<int> list{vec.size()};

    std::copy(vec.begin(), vec.end(), list.begin());

    // list = 1, 2, 3, 4
}

However, as you may note, it means that: 但是,正如您可能注意到的,这意味着:

  • you first create len(source range) default elements 首先创建len(source range)默认元素
  • and then copy elements from the source range to the destination range 1 by 1 然后将元素从源范围复制到目标范围1 x

which is rather inefficient, and requires that elements can be default constructed and then assigned to. 这是非常低效的,并且要求元素可以默认构造然后分配给。

Instead, back_insert_iterator is a fake iterators, which operates as an Adaptor over a regular container. 相反, back_insert_iterator是一个迭代器,它作为普通容器上的适配器运行。 If you look at the interface you will see that it does not behave as a regular iterator at all, and just calls push_back on the underlying container reference it embeds whenever you try to push an item in. 如果你看一下界面,你会发现它根本不像常规迭代器那样,只要你试图推送一个项目,就会在它嵌入的底层容器引用上调用push_back

int main() {
    std::list<int> list;

    std::back_insert_iterator<std::list<int>> bii(list);

    bii = 1;
    bii = 2;
    bii = 3;
    bii = 4;

    // list = 1, 2, 3, 4

    // note: decltype(*bii) == bii&, so deferencing bii serves no purpose;
    // similarly, ++bi does nothing either; both operations are just defined
    // to mimick a regular operator interface so it can be used in regular
    // algorithms over iterators.
}

Thus, the two approaches are equally valid, but have different behaviors: 因此,这两种方法同样有效,但具有不同的行为:

  • a regular iterator allows you to overwrite an existing range 常规迭代器允许您覆盖现有范围
  • a back_insert_iterator allows you to append to an existing container back_insert_iterator允许您追加到现有容器

The semantics are different, picks whichever makes sense for the task at hand. 语义是不同的,选择对手头的任务有意义。

Regular iterators don't know anything about the container they are being used with except for the data type its holding. 常规迭代器对它们正在使用的容器一无所知,除了它所持有的数据类型。 In order to add elements to a container, let's say a vector, one needs to know the number of elements in the vector. 为了向容器添加元素,让我们说一个向量,需要知道向量中的元素数量。

They can, but it may not be safe to do so. 它们可以,但这样做可能不安全。

I suggest reading the excellent introduction to iterators from the Helsinki University. 我建议阅读赫尔辛基大学对迭代器的精彩介绍

If you have an iterator for a container (forward, bidirectional and random access are all fine), and you use it as an output iterator in an STL algorithm context, the output will be written into the container, but the iterator is never compared against the container's end() . 如果你有一个容器的迭代器(正向,双向和随机访问都很好),并且你在STL算法上下文中使用它作为输出迭代器,输出将被写入容器, 迭代器永远不会被比较容器的end() If all the elements written fit, then this is fine, but if not, the output iterator will reach end() , and dereferencing it to write the next element will give undefined behaviour. 如果所有写入的元素都适合,那么这很好,但如果没有,输出迭代器将到达end() ,并且取消引用它来写下一个元素将给出未定义的行为。

Things like back_insert_iterator are designed specifically for use as an output iterator, and will not cause UB in this way, because they can always insert more elements. back_insert_iterator这样的东西专门用作输出迭代器,并且不会以这种方式导致UB,因为它们总是可以插入更多元素。

General iterators don't change the size or structure of a sequence. 常规迭代器不会更改序列的大小或结构。 In particular random access iterators just access the elements at specific locations. 特别是随机访问迭代器只访问特定位置的元素。

std::back_insert_iterator<Cont> is a trmplate modelling a concrete output iterator changing the sequence it refers to for each element written to it: it calls cont.push_back() for each element written. std::back_insert_iterator<Cont>是一个trmplate,它建模一个具体的输出迭代器,改变它所引用的每个元素所引用的序列:它为每个写入的元素调用cont.push_back() Since the iterator doesn't read the sequence it is modifying, adding elements works quite nicely. 由于迭代器不会读取它正在修改的序列,因此添加元素的效果非常好。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM