developer tip

std :: set에 "contains"멤버 함수가없는 이유는 무엇입니까?

copycodes 2020. 8. 26. 08:00
반응형

std :: set에 "contains"멤버 함수가없는 이유는 무엇입니까?


나는 많이 사용 std::set<int>하고 있으며 종종 그러한 세트에 숫자가 포함되어 있는지 확인해야합니다.

다음과 같이 쓰는 것이 자연 스럽습니다.

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

하지만 contains멤버 가 없어서 귀찮은 글을 써야합니다.

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

또는 명확하지 않은 경우 :

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

이 디자인 결정에 대한 이유가 있습니까?


나는 그들이 만들려고 노력했기 때문에 아마 생각 std::setstd::multiset최대한 유사. (그리고 분명히 count에 대해 완벽하게 합리적인 의미를 가지고 std::multiset있습니다.)

개인적으로 이것은 실수라고 생각합니다.

count철자가 틀린 contains하고 테스트를 다음과 같이 작성하면 그렇게 나쁘게 보이지 않습니다 .

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

그래도 여전히 부끄러운 일입니다.


쓸 수 있도록 if (s.contains()), contains()반환합니다 bool(로 변환 또는 유형 bool, 또 다른 이야기이다)처럼 binary_search않습니다.

근본적인 이유 디자인 결정 뒤에 하지 이런 식으로 그것을 할 수는 있다는 것입니다 contains()A는 반환 bool요소가 컬렉션에 위치에 대한 유용한 정보를 잃게 . find()반복기의 형태로 해당 정보를 보존하고 반환하므로 STL과 같은 일반 라이브러리에 더 적합합니다. Alex Stepanov는 종종 설명했듯이 (예 : 여기 ) 이것은 항상 Alex Stepanov의 기본 원칙이었습니다 .

count()일반적으로 접근 방식에 관해서 는 괜찮은 해결 방법이지만 문제 는해야하는 것 보다 더 많은 작업 contains() 을 수행한다는 것 입니다.

그렇다고해서 a bool contains()가 가지고 있어도 좋고 필요하지도 않다는 은 아닙니다. 얼마 전에 ISO C ++ 표준-미래 제안 그룹에서이 문제에 대해 긴 토론을했습니다 .


아무도 추가하지 않았기 때문에 부족합니다. 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)) {
}

기본적 std으로이 기술을 사용하여 대부분의 C ++ 유형에 대한 확장 메서드를 작성할 수 있습니다 .

It makes a lot more sense to just do this:

if (some_set.count(some_element)) {
}

but I am amused by the extension method method.

The really sad thing is that writing an efficient contains could be faster on a multimap or multiset, as they just have to find one element, while count has to find each of them and count them.

A multiset containing 1 billion copies of 7 (you know, in case you run out) can have a really slow .count(7), but could have a very fast contains(7).

With the above extension method, we could make it faster for this case by using lower_bound, comparing to end, and then comparing to the element. Doing that for an unordered meow as well as an ordered meow would require fancy SFINAE or container-specific overloads however.


You are looking into particular case and not seeing bigger picture. As stated in documentation std::set meets requirement of AssociativeContainer concept. For that concept it does not make any sense to have contains method, as it is pretty much useless for std::multiset and std::multimap, but count works fine for all of them. Though method contains could be added as an alias for count for std::set, std::map and their hashed versions (like length for size() in std::string ), but looks like library creators did not see real need for it.


Although I don't know why std::set has no contains but count which only ever returns 0 or 1, you can write a templated contains helper function like this:

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();
}

And use it like this:

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

The true reason for set is a mystery for me, but one possible explanation for this same design in map could be to prevent people from writing inefficient code by accident:

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

Which would result in two map lookups.

Instead, you are forced to get an iterator. This gives you a mental hint that you should reuse the iterator:

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

which consumes only one map lookup.

When we realize that set and map are made from the same flesh, we can apply this principle also to set. That is, if we want to act on an item in the set only if it is present in the set, this design can prevent us from writing code as this:

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();
}

Of course all this is a mere speculation.


What about 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;

Another reason is that it would give a programmer the false impression that std::set is a set in the math set theory sense. If they implement that, then many other questions would follow: if an std::set has contains() for a value, why doesn't it have it for another set? Where are union(), intersection() and other set operations and predicates?

The answer is, of course, that some of the set operations are already implemented as functions in (std::set_union() etc.) and other are as trivially implemented as contains(). Functions and function objects work better with math abstractions than object members, and they are not limited to the particular container type.

If one need to implement a full math-set functionality, he has not only a choice of underlying container, but also he has a choice of implementation details, e.g., would his theory_union() function work with immutable objects, better suited for functional programming, or would it modify its operands and save memory? Would it be implemented as function object from the start or it'd be better to implement is a C-function, and use std::function<> if needed?

As it is now, std::set is just a container, well-suited for the implementation of set in math sense, but it is nearly as far from being a theoretical set as std::vector from being a theoretical vector.

참고URL : https://stackoverflow.com/questions/42532550/why-does-stdset-not-have-a-contains-member-function

반응형