STL 中的某些容器没有查找函数
some containers in stl don't have find function
一些STL容器(如std::list
和std::vector
)没有find()
方法作为成员函数。为什么?我知道从<algorithm>
中可以选择使用std::find
,但这种使用并不是100%自然的。
总体设计原则是尽可能使用std::find
,并在更高效时实现find
成员功能。
确实具有find
成员的容器是具有比在std::find
中执行的线性搜索更有效的元素查找机制的容器。例如,诸如std::set
和std::map
之类的二进制搜索树,或者诸如它们的对应unordered
之类的哈希表。
特别要注意的是, 除了效率问题之外,值得注意的是,一些容器: 本质上要么已排序( 公开支持前向迭代和/或随机接入 可能私下支持二进制搜索 有这样一个专门的用于元素访问的公共API,因此算法的潜在重用是相对有限的(例如 同样令人感兴趣的是,可以使用许多算法在 CCD_ 31需要一个排序的向量,但为了实现O(log2n)的复杂度而四处跳跃。 其他选项,如外推搜索和散列可能适用 您将如何选择实现和添加哪些成员?看起来有点武断。尽管如此,从性能角度来看,使用哪种元素的选择可能很重要:对于一百万个元素, 具有find
、lower_bound
和upper_bound
成员函数仅在比使用非成员等价物效率更高时提供,或者在非成员无法操作std::string
具有find
功能,该功能为字符搜索提供类似std::find()
的线性搜索功能,为子字符串搜索提供类似于std::search()
的功能:虽然非成员版本可能具有相同的big-O效率,但如果专用机器代码指令经常可用于"字符串"搜索,它们的效率可能会更低。还有历史、方便和易于移植的因素。multi
-set
,map
)要么未排序(unordered_map
,unordered_set
),通常未排序(例如std::string
),或者很容易(std::vector
)unordered_map::bucket
/::begin(n)
等)vector
中进行搜索:std::find
进行强力线性O(n)搜索,它将首先"找到"较低索引元素,find
在匹配前平均进行五十万个元素的比较,当元素不在时平均进行一百万个元素的对比,而binary_search
通常会进行约20次比较。find
的容器通常不提供这样的灵活性,并且它们提供的find
和/或lower_bound
/upper_bound
可以被视为非成员等价物的替代品,并且可能是搜索容器的唯一合理方式。
因为algorithm
中的std::find
函数适用于它们。
通常,像std::vector
和std::list
这样的容器具有线性搜索时间复杂性。因此,将成员find
函数附加到它们是冗余的,因为已经存在std::find
。对于其他容器(例如,std::set
或std::map
等),有更好的方法(即,比线性复杂度更快)来实现搜索。因此,实现者将这些更快的搜索算法实现为成员函数。
具有按关键字搜索功能的容器将集成查找方法(例如,使用可以有效查找的二进制树在内部实现的映射)。
其他的,如您所引用的,将允许使用std::find进行范围搜索,但没有特色的find函数,因为它与std::find相比没有算法优势(排序/特殊情况除外)
std::vector
、std::list
、std::forward_list
等容器是顺序容器。没有什么比顺序搜索更好的方法可以应用于这些容器了。因此,如果每个顺序容器的顺序搜索对于所有这些容器都是相同的,则无需重写顺序搜索。
std::basic_string
类是一个例外,它最初模拟的C字符串已经具有strchr、strstr和其他特殊搜索函数。
对不同的容器使用相同的函数可以使API更加清晰,您不必学习每个容器的特性,只需学习如何将一个函数应用于所有容器。
这也是为了代码的可重用性-您使用的算法从提供迭代器的任何容器中获取迭代器,因此该算法不必依赖于容器是std::vector
、std::list
等。
如其他评论中所述,设计原理是vector::find()
可以像非成员函数std::find()
一样高效地实现。使用后者的好处是,它将数据结构和作用于数据结构的运算符解耦,从而提高了可维护性(这对库的开发人员来说是有利的)。
然而,前者的好处是,它将使所有容器之间的API保持一致,并使客户端代码不那么冗长。这将提高可学习性和可读性(这对图书馆的用户来说是有利的)。此外,一致的API将允许编写通用代码。考虑一下:
template <typename Container, typename T>
void foo(const Container& c, const T& x) {
if (std::find(c.begin(), c.end(), x) != c.end()) {
// ...
}
}
当CCD_ 54是CCD_ 55或CCD_。为了提高效率,我们需要做:
template <typename Container, typename T>
void foo(const Container& c, const T& x) {
if (c.find(x) != c.end()) {
// ...
}
}
但是它不编译std::vector
和std::list
。这给库的用户带来了负担,他们需要为他们想要支持的每种类型手动编写专用/重载的通用函数:
template <typename T>
bool contains(const std::vector<T>& c, const T& x) {
return std::find(c.begin(), c.end(), x) != c.end();
}
template <typename T>
bool contains(const std::set<T>& c, const T& x) {
return c.find(x) != c.end();
}
template <typename Container, typename T>
void foo(const Container& c, const T& x) {
if (contains(c, x)) {
// ...
}
}
我承认做出这些类型的设计决策很难,但我的观点是STL的设计师在这里犯了一个错误。非常小的可维护性负担似乎在很大程度上值得为用户提供更好的API和一致性。简而言之,由于find
必须是某些容器的成员函数(为了性能),因此find
应该是所有容器的成员功能(为了一致性)。请注意,我完全同意其他算法是非成员函数。
(我的意思是,拜托,容器从定义上来说是包含东西的东西。对于用户来说,编写一个通用且高效的"包含"函数应该是微不足道的。事实上,我认为应该将其添加到容器概念中,但我离题了。)
- 递归ASMVisitor 和查找函数调用站点
- C++ - 查找函数无法在子字符串上执行
- 查找函数是否为常量
- GDB 如何查找函数退出的位置
- 查找函数在unordered_map中的工作方式是搜索键值
- 在 C++ <algorithm>中查找函数
- 错误:二进制表达式的操作数无效(映射使用查找函数错误)
- 哪个查找规则阻止编译器查找函数
- 在C++中查找函数的调用方(Visual Studio)
- 关于获取行和字符串查找函数的问题
- 查找函数在失败结果中应该返回什么
- 如何使用调用和别名指令在 llvm 字节码中查找函数名称
- std::字符串类查找函数不返回预期结果.我可能用错了
- 在成对向量中查找函数时出错
- 从堆栈指针中查找函数参数值
- 如何定义查找函数
- 如何查找函数是否可重入
- 在c++中查找函数
- GNU g++ 4.9.2 查找函数调用的编译错误
- 使用GDB查找函数对应的内存地址/调试