为什么std :: map没有find / lower_bound的重载,std :: list没有sort的重载?

时间:2014-03-07 20:01:52

标签: c++ set template-specialization lower-bound

我知道您不应该使用std::find(some_map.begin(), some_map.end())std::lower_bound,因为它需要线性时间而不是some_map.lower_bound提供的对数。类似的事情发生在std::list:有std::list::sort函数用于排序,但是你无法调用std::sort(some_list.begin(), some_list.end()),因为迭代器不是随机访问。

但是,例如std::swap对标准容器有重载,因此swap(some_map, other_map)的调用需要O(1)而不是O(n)。为什么C ++标准没有为地图和集合提供lower_boundfind的专用版本?有充分的理由吗?

4 个答案:

答案 0 :(得分:5)

我认为没有任何深层原因,但它比任何事物更具哲学性。标准算法的自由函数形式,包括你提到的算法,采用迭代器对来表示它们将遍历的范围。算法无法从这些迭代器中确定底层容器的类型。

提供专业化或重载将偏离此模型,因为您必须将容器本身传递给算法。

swap是不同的,因为它将涉及的类型的实例作为参数,而不仅仅是迭代器。

答案 1 :(得分:3)

STL遵循的规则是自由函数算法对迭代器进行操作并且是通用的,而不关心迭代器所属的范围类型(​​迭代器类别除外)。如果特定容器类型可以比使用std::algo(cont.begin(), cont.end())更有效地实现操作,则该操作作为容器的成员函数提供,并被称为cont.algo()

所以你有std::map<>::lower_bound()std::map<>::find()以及std::list<>::sort()

答案 2 :(得分:2)

这些函数应该在迭代器对方面起作用的简单事实会产生相当大的开销,即使专门用于map / set迭代器也是如此。

请记住:

  • 可以使用任何一对迭代器调用此类函数,而不仅仅是开始/结束。

  • map / sets的迭代器通常实现为指向 leaf 节点的指针,而成员find / lower_bound的起始节点是<树的strong> root 节点。

成员find和lower_bound更好,因为指向根节点的指针直接存储为map / set对象的成员。

假设的非成员find必须遍历树以找到两个输入节点之间的最低共同祖先,然后执行dfs - 同时仍然小心只搜索[first,last] )范围 - 这显然更贵。

是的,您可以跟踪迭代器中的根节点,然后优化是否使用开始/结束对调用函数...只是为了避免成员函数?

答案 3 :(得分:1)

没有提供那些超载的良好“设计理念”原因。特别是,整个STL容器/算法交互被设计为通过不同的迭代器概念,使得这两个部分保持独立,这样您就可以定义新的容器类型而不必为所有算法重载,从而您可以定义新算法,无需为所有容器重载。

除了这些设计选择之外,还有一个非常好的技术原因可以解释为什么这些算法(排序,下限等等)不能为listmap / {等特殊容器重载{1}}。原因是通常没有理由将迭代器显式连接到其父容器。换句话说,您可以从列表容器中获取列表迭代器(开始/结束),但是您无法从列表迭代器获取(引用)列表容器。 map / set迭代器也是如此。可以将迭代器实现为对其父容器“盲目”。例如,像set这样的容器通常包含作为数据成员的指向head和tail节点以及allocator对象的指针,但是迭代器本身(通常只是指向节点的指针)不需要知道关于头/尾或分配器,他们只需要走前一个/下一个链接指针来在列表中来回移动。因此,如果要为list容器实现std::sort的重载,则无法从传递给sort函数的迭代器中检索列表容器。并且,在您提到的所有情况中,适合于这些特殊容器的算法版本是“集中式”的,因为它们需要在容器级别运行,而不是在“盲”迭代器级别运行。这就是为什么它们作为成员函数有意义,这就是为什么你不能从迭代器中获取它们的原因。

但是,如果您需要使用通用方法来调用这些算法,而不管您拥有哪个容器,那么您可以根据需要在容器级别提供重载的函数模板。这很容易:

list

这里的要点是,如果你已经与STL容器捆绑在一起,那么你可以用这种方式配合STL算法,如果你想......但是不要指望C ++标准库强迫你拥有容器和算法之间的这种相互依赖,因为不是每个人都想要它们(例如,很多人真的喜欢STL算法,但对STL容器不满意(有很多问题))。

相关问题