iterator和back_insert_iterator有什么区别?

时间:2013-11-11 13:33:36

标签: c++ stl

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

5 个答案:

答案 0 :(得分:11)

std::back_insert_iterator输出迭代器的特定种类,支持push_back操作。当您使用operator= 时,它会将值push_backs到基础容器中 - 因此,在这种意义上,它充当具有push_back成员函数的容器的适配器。

一个例子很容易理解:

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

输出:

10 99

Online Demo

++上的常规迭代器操作*it无效。

但你很少直接使用它们(我从未直接使用它直到现在)。您可以将它们与算法一起使用,例如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));

您还希望看到以下(适配器)迭代器:

因此,根据容器,选择适配器迭代器。

请注意,所有这些都是输出迭代器。

  

为什么不能在std :: copy()这样的通用算法中使用它们而不是使用back_insert_iterator。

当然,您可以在std::copy等算法中使用随机访问迭代器(或任何输出迭代器)作为第三个参数,但假设迭代器引用现有范围 - *it++it已定义为您传递的值。您将它们传递给覆盖范围的现有元素,而std::back_insert_iterator 新元素添加到容器中。

希望有所帮助。

答案 1 :(得分:3)

实际上,您可以在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
}

但是,正如您可能注意到的,这意味着:

  • 您首先创建len(source range)默认元素
  • 然后将元素从源范围复制到目标范围1 x 1

这是相当低效的,并且要求可以默认构造元素然后将其分配给。

相反,back_insert_iterator是一个迭代器,它作为普通容器上的适配器运行。如果查看interface,您将看到它根本不像常规迭代器那样,只要在尝试推送项目时嵌入的底层容器引用上调用push_back。 / p>

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.
}

因此,这两种方法同样有效,但行为不同:

  • 常规迭代器允许您覆盖现有范围
  • back_insert_iterator允许您追加到现有容器

语义不同,选择对手头的任务有意义。

答案 2 :(得分:0)

常规迭代器对它们正在使用的容器一无所知,除了它所持有的数据类型。为了向容器添加元素,让我们说一个向量,需要知道向量中元素的数量。

答案 3 :(得分:0)

他们可以,但这样做可能不安全。

我建议阅读赫尔辛基大学的优秀introduction to iterators

如果你有一个容器的迭代器(正向,双向和随机访问都可以),并且你在STL算法上下文中将它用作输出迭代器,输出将被写入容器但是迭代器永远不会与容器的end()进行比较。如果所有写入的元素都适合,那么这很好,但如果没有,输出迭代器将达到end(),并且取消引用它来写下一个元素将给出未定义的行为。

back_insert_iterator这样的东西专门用作输出迭代器,并且不会以这种方式导致UB,因为它们总是可以插入更多元素。

答案 4 :(得分:0)

常规迭代器不会更改序列的大小或结构。特别是随机访问迭代器只访问特定位置的元素。

std::back_insert_iterator<Cont>是一个模拟具体输出迭代器的trmplate,它改变了它为每个写入它的元素引用的序列:它为每个写入的元素调用cont.push_back()。由于迭代器不会读取它正在修改的序列,因此添加元素的效果非常好。