在STL容器(例如vector)中简洁地检查项是否存在的C++方法

9
bool xInItems = std::find(items.begin(), items.end(), x) != items.end();

有没有更简洁的方法来检查x是否在items中?这种方式似乎过于冗长(重复了三次items),使得代码的意图有点难以阅读。

例如,是否存在以下类似的方法:

bool xInItems = boost::contains(items, x);

如果没有更简洁的boost/stl算法来检查集合是否包含某个项,那么使用一个辅助函数来启用contains(items, x)是好还是坏的做法呢?

我是否使用了错误的STL容器?即使使用std::set也会导致 bool xInItems = items.find(x) != items.end(); 这仍然显得冗长。我是否想错了呢?


1
对于 std::set,也许可以使用 bool xInItems = (items.count(x) == 1);。对于其他情况,boost::find 可能是最好的选择。 (STL 和 boost 都有一个 count(),但它们不够高效,因为它们必须遍历整个范围。) - T.C.
3
大部分相关标准库函数都是设计用于对迭代器对进行操作,而不是整个容器。这样做可以在灵活性方面获得更多的优势,但代价是不够简洁。如果需要的话,您当然可以自己编写辅助函数(我也是这么做的),但是这样就必须在各处包含它们的头文件... - dlf
如果有人能解释一下这些负评是怎么回事,我会很感激。我在 stack overflow 上发帖还比较新手。谢谢。 - JDiMatteo
我怀疑这个问题已经存在了。如果是这样,那可能就是为什么会有负评的原因。 - dlf
2
我认为你正在寻找范围(请参阅)。它们可能会(或可能不会)最终成为C++标准。与此同时,如果您不喜欢“迭代器对”接口,则最好自己编写或使用Boost.Range。 - Nemo
显示剩余4条评论
6个回答

7

从零开始编写模板函数并不难。

template<typename T, typename Iterator>
bool contains(Iterator it1, Iterator it2, const T & value)
{
    return std::find(it1, it2, value) != it2;
}

template<typename T, typename Container>
bool contains(const Container & c, const T & value)
{
    return contains(c.begin(), c.end(), value);
}

您甚至可以为具有自己的find函数的容器提供专门的特化,以便它不会调用std::find


6
要使上述代码更具“工业级”水平:将第一行的c.begin()替换为using std::begin;,并将 begin(c)替换为begin(c).end()同理)(C++11)。其次,我发现返回指向元素的指针(或optional)很有用--当转换为bool时,它会给出正确的值,并且可以使用if(int* x = contains(vec,3))(我使用optional<T&>)。添加标签支持以将关联容器从序列容器中分离,以使用关联容器的find方法而不是线性搜索。 - Yakk - Adam Nevraumont
1
问题不是如何编写一个带有模板的contains函数,而是标准库或boost中是否已经存在这样一个函数,以及自己编写是否是良好的实践。尽管如此,我还是会给+1,因为这很有用。 - JDiMatteo

4

any_of_equal可以完成这个任务:

#include <boost/algorithm/cxx11/any_of.hpp>

bool xInItems = boost::algorithm::any_of_equal(items, x);

3

C++20中的stl容器setmapunordered_map都有contains方法:

bool xInItems = items.contains(x);

不幸的是,这并非所有容器都适用,因此对于vectorlist,您仍需要按照您已经完成的方式编写它(或者像Mark的好答案中那样将其包装成一个函数)。


1
如果你的数据已经排序,你可以使用 std::binary_search,它会返回一个 bool 值:
bool xInItems = std::binary_search(items.begin(), items.end(), x));

如果你确实需要让项目保持未排序状态,但已经使用了C++11,那么可以使用std::any_of,但它需要一个谓词,因此它很可能会变得至少和std::find一样冗长(甚至更多)。

std::binary_search 返回一个布尔值,因此它更加简洁,这就是我选择这个答案而不是其他答案的原因。但我认为真正的收获是不必担心迭代器对的简洁性,因为迭代器对是惯用的 C++。仍有希望在未来的标准中接受 std::range,使得更加简洁的惯用 C++ 成为可能。 - JDiMatteo

1

一个简单的方法来确定一个元素是否在一个集合中,就是使用:

container.find(x) != container.end()

如果你要使用一组整数,它可能是这样的:

stl::set<int> intSet;
intSet.insert(3);
if( intSet.find(3) != intSet.end()) 
      printf("Found it!");

0
#include "boost/range/algorithm/find.hpp"

bool xInItems = boost::find(items, x) != items.end();

考虑到 C++ 中对灵活迭代器使用的偏好,这已经足够简洁了。

你应该只使用 std::find,因为它是惯用语,希望最终 std::range 能够被接受并提供一种更简洁的替代迭代器对的标准。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接