我正在大量使用,std::set<int>而且我经常需要检查这样的集合是否包含数字.
我觉得写作很自然:
if (myset.contains(number))
...
Run Code Online (Sandbox Code Playgroud)
但由于缺少一名contains成员,我需要编写繁琐的内容:
if (myset.find(number) != myset.end())
..
Run Code Online (Sandbox Code Playgroud)
或者不那么明显:
if (myset.count(element) > 0)
..
Run Code Online (Sandbox Code Playgroud)
这个设计决定有理由吗?
Mar*_*ica 146
我想这可能是因为他们试图制作std::set并std::multiset尽可能相似.(显然count有一个非常明智的意义std::multiset.)
我个人认为这是一个错误.
如果你假装count只是拼写错误contains并将测试编写为:它看起来并不那么糟糕:
if (myset.count(element))
...
Run Code Online (Sandbox Code Playgroud)
尽管如此,这仍然是一种耻辱.
Leo*_*aar 41
为了能够写if (s.contains()),contains()必须返回一个bool(或一个类型可转换为bool,这是另一个故事),就像binary_search.
设计决策不以这种方式做的根本原因是返回a 会丢失关于元素在集合中的位置的有价值信息.以迭代器的形式保存和返回该信息,因此对于像STL这样的通用库来说是更好的选择.这一直是Alex Stepanov的指导原则,正如他经常解释的那样(例如,这里).contains()boolfind()
至于count()方法,虽然它通常是一个好的解决方法,但它的问题在于它做的工作比 contains() 不得不做的更多.
这并不是说a bool contains()不是一个非常好的甚至是必要的.不久前,我们在ISO C++标准 - 未来提案组中对此同样的问题进行了长时间的讨论.
Yak*_*ont 22
它缺乏它,因为没有人添加它.没有人添加它,因为来自STL的容器,std库被设计为在界面上设计得最小.(注意,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)};
}
Run Code Online (Sandbox Code Playgroud)
使用:
if (some_set->*contains(some_element)) {
}
Run Code Online (Sandbox Code Playgroud)
基本上,您可以std使用此技术为大多数C++ 类型编写扩展方法.
这样做更有意义:
if (some_set.count(some_element)) {
}
Run Code Online (Sandbox Code Playgroud)
但我对扩展方法方法很有兴趣.
真正令人伤心的是,写一个有效的contains可能会更快multimap或者multiset,因为他们只需要找到一个元素,而count必须找到它们并计算它们.
一个包含10亿份7的多重集(你知道,如果你用完了)可能会非常慢.count(7),但可能会非常快contains(7).
使用上面的扩展方法,我们可以通过使用lower_bound,比较end,然后与元素进行比较,使这种情况更快.对于无序的喵喵和有序的喵喵来说,这需要花哨的SFINAE或容器特定的重载.
Sla*_*ica 12
你正在研究特定的情况,而不是看到更大的图片.如文档中所述 std::set满足AssociativeContainer概念的要求.对于这一概念它没有任何意义,有contains方法,因为它是几乎无用的std::multiset和std::multimap,但count对所有的人都工作正常.虽然方法contains可以被添加为一个别名count为std::set,std::map并且其散列版本(如length用于size()在std::string),但看起来像库创建者没有看到它真正的需要.
rus*_*tyx 10
虽然我不知道为什么std::set没有,contains但count只返回0或者1,你可以写一个模板化的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();
}
Run Code Online (Sandbox Code Playgroud)
并像这样使用它:
if (contains(myset, element)) ...
Run Code Online (Sandbox Code Playgroud)
真正的原因对set我来说是一个谜,但对同一设计的一个可能的解释map可能是防止人们意外地编写低效的代码:
if (myMap.contains("Meaning of universe"))
{
myMap["Meaning of universe"] = 42;
}
Run Code Online (Sandbox Code Playgroud)
这将导致两次map查找.
相反,你被迫获得一个迭代器.这给了你一个精神暗示你应该重用迭代器:
auto position = myMap.find("Meaning of universe");
if (position != myMap.cend())
{
position->second = 42;
}
Run Code Online (Sandbox Code Playgroud)
它只消耗一次map查找.
当我们意识到set并且map是由相同的肉体制成时,我们也可以应用这个原则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();
}
Run Code Online (Sandbox Code Playgroud)
当然这一切只是猜测.
| 归档时间: |
|
| 查看次数: |
11838 次 |
| 最近记录: |