std :: forward_list和std :: forward_list :: push_back

时间:2012-01-05 12:25:16

标签: c++ c++11 std forward-list

我想使用std::forward_list

由于:

  

转发列表是一个支持快速插入和删除的容器   来自容器的任何地方的元素

但是没有* std :: forward_list :: push_back *实现。

是否有一种高性能的方式来增加对单一或无理由的支持?

5 个答案:

答案 0 :(得分:26)

我建议反对std::forward_list,就像我在几乎所有情况下都建议反对std::list一样。就个人而言,我从未在我的代码中发现链接列表是最佳数据结构的情况。

在C ++中,您的默认首选数据集应为std::vector。它为您提供高效push_back,如果这是您真正需要的。如果您只查看该操作的抽象大O复杂度测量,从技术上讲,它不能从中间有效地删除和插入。然而,在现实世界中,std::vector仍然赢得,即使在中间插入和删除

例如,Bjarne Stroustrup创建了一个100,000元素std::liststd::vector的测试。他会搜索每个元素并删除它。然后他会找到一个插入点并插入中间。他可以在std::vector上使用二分搜索,但不会使比较“更公平”。

结果显示std::vector获胜,即使在std::list强大的情况下也是如此。简单地遍历std::list需要更长的时间,因为所有对象在内存中的距离是多远。 std::list不是缓存友好的,这可能是现代处理器最重要的事情。

The complete talk by Bjarne Stroustrup

Thorough explanation of the effects, with benchmarks at multiple sizes

请注意,此处的第二个链接提供了一些您可能想要使用std::list的情况,例如元素的大小很大时。但是,我一直处于这样一种情况:我按特定顺序拥有许多元素,需要删除一些元素。

这些元素比任何内置类型都要大,但不是很大,在32位计算机上可能各占20-30个字节。元素的数量足够大,因此我的整个数据结构只有几百MiB。数据收集是一组基于当前已知信息理论上可以有效的值。该算法迭代所有元素并删除了基于新信息不再有效的元素,每次传递可能会删除大约80%的剩余元素。

我的第一个实现是一个简单的std::vector方法,我在遍历时删除了无效元素。这适用于小型测试数据集,但是当我尝试做真实的事情时,它太慢而无法使用。我切换到std::list作为容器,但使用相同的算法,我看到了显着的性能改进。但是,它仍然太慢而无法使用。获胜的更改是切换回std::vector,但是我没有删除不合适的元素,而是创建了一个新的std::vector,并且我发现任何好的元素都放入了std::vector std::vector。 1}},然后在函数结束时我会简单地丢弃旧的std::list并使用新的std::list,这给了我std::vector与{{1}}一样快的速度{1}}让我完成了我原来的{{1}}实施工具,这个速度非常快,非常有用。

答案 1 :(得分:17)

std::forward_list支持快速插入和删除,但不支持遍历。要实现.push_back,您首先需要到达列表的末尾,即O(N)并且根本不快,这可能是它未实现的原因。

您可以通过递增.before_begin N次

找到最后一个元素的迭代器
auto before_end = slist.before_begin();
for (auto& _ : slist)
  ++ before_end;

然后使用.insert_after.emplace_after插入元素:

slist.insert_after(before_end, 1234);

答案 2 :(得分:6)

std :: forward_list的要点是std :: list的超精简版本,因此它不会将迭代器存储到最后一个元素。如果你需要,你必须自己维护它,如下:

forward_list<int> a;

auto it = a.before_begin();

for(int i = 0; i < 10; ++i)
    it = a.insert_after(it, i);

答案 3 :(得分:5)

没有push_back因为列表没有跟踪列表的后面,只跟踪前面。

您可以在列表周围编写一个包装器来维护最后一个元素的迭代器,并使用push_backinsert_after实现push_front,具体取决于列表是否为空。如果您想支持更复杂的操作(例如sortsplice_after),这将变得相当复杂。

或者,如果您不需要push_back快速,则可以直接在线性时间内完成。

除非内存非常严格,否则最佳解决方案是使用list。这与forward_list具有相同的性能特征,并且是双向的,支持push_back以及push_front;成本是每个元素的额外指针。

答案 4 :(得分:0)

不幸的是,我无法添加评论(声望很低),但我只是想提一下forward_list&amp; list的优点是insert-delete操作不会使迭代器失效。我有一个应用程序,在迭代和处理单个元素的同时,元素集合正在增长。没有迭代器失效允许我实现段扫描(列表的开头作为段的开始,最后一段的开始作为结束)。