如果随机访问迭代器可用于访问相对于它们指向的元素的任意偏移位置处的元素(有点像指针),为什么它们不能在通用算法中使用,例如std::copy()
而不是 using back_insert_iterator
,两者之间有什么区别?
5 回答
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
在线演示。
通常的迭代器操作++
和*
onit
没有效果。
但是您很少直接使用它们(直到现在我还没有直接使用它)。您可以将它们与算法一起使用,例如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::front_insert_iterator n 支持
push_front
操作 - std::insert_iterator支持
insert
操作。
因此,根据容器,您选择适配器迭代器。
请注意,它们都是输出迭代器。
为什么不能在 std::copy() 等通用算法中使用它们,而不是使用 back_insert_iterator。
当然,您可以在算法中使用随机访问迭代器(或任何输出迭代器),例如std::copy
, 作为第三个参数,但这假设迭代器引用现有范围 -*it
并且++it
为您传递的值定义良好。您传递它们以覆盖范围的现有元素,同时std::back_insert_iterator
将新元素添加到容器中。
希望有帮助。
实际上,您完全可以在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
它是一个假迭代器,它在常规容器上作为适配器运行。如果您查看该接口,您会发现它根本不像常规迭代器,而是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.
}
因此,这两种方法同样有效,但具有不同的行为:
- 常规迭代器允许您覆盖现有范围
- a
back_insert_iterator
允许您附加到现有容器
语义不同,选择对手头任务有意义的。
他们可以,但这样做可能不安全。
我建议阅读赫尔辛基大学对迭代器的精彩介绍。
如果您有一个容器的迭代器(前向、双向和随机访问都可以),并且您在 STL 算法上下文中将其用作输出迭代器,则输出将被写入容器中,但永远不会与迭代器进行比较容器的end()
. 如果所有写入的元素都适合,那么这很好,但如果不适合,输出迭代器将达到end()
,并且取消引用它以写入下一个元素将给出未定义的行为。
likeback_insert_iterator
是专门为用作输出迭代器而设计的,不会以这种方式导致 UB,因为它们总是可以插入更多元素。
通用迭代器不会改变序列的大小或结构。特别是随机访问迭代器只访问特定位置的元素。
std::back_insert_iterator<Cont>
是一个模拟具体输出迭代器的 trmplate,它为写入的每个元素更改它所引用的序列:它调用cont.push_back()
每个写入的元素。由于迭代器不读取它正在修改的序列,因此添加元素效果很好。
常规迭代器不知道与它们一起使用的容器的任何信息,除了它所持有的数据类型。为了向容器中添加元素,比如说一个向量,我们需要知道向量中元素的数量。