为什么不在c ++容器中实现包含函数?

Why not implement contains function in c++ Containers?

我的第一个问题是:为什么C++中EDCOX1的0个函数在EDOCX1中丢失?1?

所以我寻找了一个解释,我发现了一些有趣的东西,关于为什么其他一些功能没有在所有Containers中实现(本质上是因为性能问题和便利性)。

我知道你可以使用algorithm库中的find函数,或者你可以用Iterator编写自己的函数,但我不明白的是,为什么在set中实现了contains函数(这里称为find,而在EDOCX1中实现了〔9〕或queue不是。

我也很清楚为什么容器类在Java中不共享像EDCOX1,11,DO这样的公共接口(由于这个答案),但是在这种情况下,我找不到为什么不在所有容器类中实现EDCOX1×0函数(或者至少在一些像EDCOX1 9)中实现的原因。

谢谢你


因为这是一个糟糕的设计模式。如果您在线性容器上重复使用"find",那么您的代码有问题。平均和最坏情况下的时间复杂性仍然是O(N),这意味着你做了一个错误的选择。

例如,std::mapstd::unordered_map具有find成员函数,允许o(logn)和o(1)查找。这是因为容器通过这些方法采用了高效的项查找:这就是容器应该如何使用的方式。

如果您已经权衡了所有选项,并决定线性容器是您的数据的最佳模型,但在极少数情况下确实需要找到一个元素,那么std::find()允许您这样做。你不应该依赖它。我将此视为Python和Java中的反模式,以及C++中的好处。

3年前,当我还是一个初学者的程序员时,我写了很多东西。我认为这是个好主意,因为python使列表项成员成为惯用的。我不太清楚。这导致我产生了糟糕的代码,直到我了解到为什么线性搜索比二进制搜索和hashmap查找效率低。编程语言或框架应该支持好的设计模式,而不鼓励坏的设计模式。启用线性搜索是一种糟糕的设计模式。


std::set实现自己的find的原因是"generic"std::find执行线性搜索,而std::set通过在o(log2n)中搜索其树表示可以做得更好。

另一方面,std::vectorstd::list的搜索速度不能超过线性时间,因此它们依赖于std::find的实现。

请注意,您仍然可以将std::find应用于std::set以线性搜索它;它只是不如使用集合自己的find成员函数那么有效。

1
2
3
std::set<int> s {1, 2, 3, 4, 5, 6};
auto res = std::find(s.begin(), s.end(), 3);
std::cout << *res << std::endl; // prints 3


由于某些原因,其他答案集中在通用的和按容器的find方法的复杂性上。但是他们没有解释手术的问题。缺少有用的实用方法的真正原因来自C++中使用不同文件类的方式。如果每个不具有任何特殊属性的容器都有contains方法以线性复杂度执行一般搜索,那么我们要么陷入每个容器头也包括的情况,要么每个容器头重新实现它自己的find的情况。]算法(更糟)。在编译每个翻译单元的过程中,在预处理器包含(即复制粘贴)每个包含的头段之后,生成的相当大的文档会增长得更大。编译需要更多的时间。而传递的相当神秘的编译错误可能会更长。当一个非成员函数可以完成任务(并且只包括你将要使用的东西)时,不让成员函数的旧原则是有原因的。

注意,最近有一个关于uniform call syntax的建议,可能允许将实用方法混合到类中。如果它投入使用,可能会编写如下扩展函数:

1
2
3
4
5
6
7
8
9
template< typename TContainer, typename TItem > bool
contains(TContainer const & container, TItem const & item)
{
     // Some implementation possibly calling container member find
     // or ::std::find if container has none...
}

::std::vector< int > registry;
registry.contains(3); // false


容器不共享"公共(继承)接口"(如在Java中)是一个很好的原因,这正是C++泛型如此强大的原因。当您只能为所有容器编写一次代码时,为什么要为每个容器编写代码?这是STL建立的主要原则之一。

如果使用容器依赖于来自公共继承接口的成员函数,则必须为每个容器编写find函数。这是浪费和糟糕的工程。好的设计表明,如果你只能在一个地方编写代码,那么你就应该这样做,因为你只需要从那个地方删除bug,在一个地方修复bug就可以在任何地方修复bug。

因此,STL背后的原理是将算法与容器分开,这样您只需编写一次算法,它就可以适用于所有容器。一旦对算法进行了调试,将对所有容器进行调试。

这种软膏的一个缺点是,由于容器的内部结构,一些容器可以做出更有效的决策。对于这些容器,已经添加了一个特定于类型的函数,它将利用这种效率。

但是大多数功能应该与容器分开。它被称为去耦(decoupling),它在促进代码重用的同时减少了错误,这通常比多态性(类似于Java容器的库使用的多态性)要多得多。