为什么标准::设置没有“包含”成员函数?

问题描述 投票:95回答:8

我大量使用std::set<int>我经常只需要检查,如果这样一组包含许多与否。

我发现它自然地写:

if (myset.contains(number))
   ...

但由于缺乏contains成员,我需要编写繁琐的:

if (myset.find(number) != myset.end())
  ..

或并不明显:

if (myset.count(element) > 0) 
  ..

是否有这样的设计决策的理由?

c++ stl stdset
8个回答
144
投票

我想这可能是因为他们试图让std::setstd::multiset尽可能相似。 (很显然countstd::multiset一个非常明智的意思。)

我个人认为这是一个错误。

如果你假装count只是contains的拼写错误,并写了测试,因为它看起来不那么糟糕:

if (myset.count(element)) 
   ...

它仍然是一个耻辱,但。


38
投票

为了能够写if (s.contains())contains()必须返回一个bool(或类型转换为bool,这是另一回事),像binary_search一样。

背后的设计决定不去做这样的根本原因在于contains()它返回一个bool将失去有关,其中元素集合中的有价值的信息。 find()蜜饯,并返回一个迭代器的形式的信息,因此对于像STL的通用库是更好的选择。这一直是亚历克斯·斯捷潘诺夫的指导原则,正如他常解释(例如,here)。

至于一般count()方法,虽然它往往是一个不错的解决方法,它的问题是,它不是一个contains()将不得不做更多的工作。

这并不是说一个bool contains()是不是一个很好的到了,甚至必要的。前一段时间,我们有一个关于在ISO C ++标准这同一个问题long discussion - 未来提案组。


22
投票

它缺乏它,因为没有人加入它。没有人加入它,因为从该std文库并入其中设计成在界面最小的STL的容器。 (请注意,std::string没有以同样的方式来自于STL)。

如果你不介意一些奇怪的语法,你可以伪造它:

template<class K>
struct contains_t {
  K&& k;
  template<class C>
  friend bool operator->*( C&& c, contains_t&& ) {
    auto range = std::forward<C>(c).equal_range(std::forward<K>(k));
    return range.first != range.second;
    // faster than:
    // return std::forward<C>(c).count( std::forward<K>(k) ) != 0;
    // for multi-meows with lots of duplicates
  }
};
template<class K>
containts_t<K> contains( K&& k ) {
  return {std::forward<K>(k)};
}

采用:

if (some_set->*contains(some_element)) {
}

基本上,你可以写使用这种技术最C ++ std类型的扩展方法。

它使许多更有意义只是这样做:

if (some_set.count(some_element)) {
}

但我通过扩展方法方法逗乐了。

真正可悲的是,写一个高效contains可能是在multimapmultiset更快,因为他们只需要找到一个元素,而count必须找到他们每个人并计数。

含有的7 1十亿份多集(你知道,如果你用完)可以有一个非常缓慢的.count(7),但可以有一个非常快的contains(7)

通过上述扩展方法,我们可以通过使用lower_bound,比较end,然后比较的元素使它成为这种情况下更快。这样做,对于一个无序喵以及有序喵需要花哨SFINAE或特定容器重载不过。


12
投票

您正在调查具体情况,并没有看到更大的画面。正如documentation std::set规定符合AssociativeContainer概念的要求。对于这一概念也没有任何意义有contains方法,因为它是std::multisetstd::multimap几乎无用,但count工作正常,所有的人。虽然方法contains可以添加为countstd::setstd::map及其散列版本(如lengthsize() std::string)的别名,但看起来像库创建者没有看到它真正的需要。


10
投票

虽然我不知道为什么std::set没有containscount它永远只返回01,你可以写这样的模板contains辅助函数:

template<class Container, class T>
auto contains(const Container& v, const T& x)
-> decltype(v.find(x) != v.end())
{
    return v.find(x) != v.end();
}

并使用它像这样:

    if (contains(myset, element)) ...

7
投票

真正的原因是set对我来说是一个谜,但在这map同一设计一个可能的解释是,以防止有人意外写入低效的代码:

if (myMap.contains("Meaning of universe"))
{
    myMap["Meaning of universe"] = 42;
}

这将导致两个map查找。

相反,你不得不让迭代器。这给你一个心理暗示,你应该重复使用迭代器:

auto position = myMap.find("Meaning of universe");
if (position != myMap.cend())
{
    position->second = 42;
}

它仅消耗一个map查找。

当我们意识到setmap是由相同的肉身,我们可以应用这个原理也set。也就是说,如果我们想在在set项目采取行动,只有当它是存在于set,这样的设计可以防止我们写代码如下:

struct Dog
{
    std::string name;
    void bark();
}

operator <(Dog left, Dog right)
{
    return left.name < right.name;
}

std::set<Dog> dogs;
...
if (dogs.contain("Husky"))
{
    dogs.find("Husky")->bark();
}

当然,这一切仅仅是一种猜测。


0
投票

什么binary_search?

 set <int> set1;
 set1.insert(10);
 set1.insert(40);
 set1.insert(30);
 if(std::binary_search(set1.begin(),set1.end(),30))
     bool found=true;

-1
投票

另一个原因是,它会给一个程序员的假象,标准::集是在数学集理论意义上的集中。如果他们实现这一点,那么其他许多问题将遵循:如果一个的std ::集包含有()的价值,为什么没有把它的另一套?哪里联盟(),路口()等一系列操作和谓词?

答案是,当然,有些设定操作都必须在(标准:: set_union()等)的功能已经实现,其他作为平凡实现为包括()。函数和函数对象的数学抽象比对象成员更好地工作,并且它们并不限于特定的容器类型。

如果一个需要实现一个完整的数学设定的功能,他不仅有底层容器的选择,也是他的实现细节进行选择,例如,将与不可变对象他theory_union()函数的工作,更适合于函数式编程,或将它修改其操作数和节省内存?难道被实现为函数对象从一开始,或者它会是更好地执行是一个C函数,并在需要时使用std ::函数<>?

因为它是现在,性病::设置仅仅是一个容器,非常适合于在数学意义上一套执行,但它几乎是远从是一个理论上的载体是一个理论的一组为标准::向量。

© www.soinside.com 2019 - 2024. All rights reserved.