std :: set에 "contains"멤버 함수가없는 이유는 무엇입니까?
나는 많이 사용 std::set<int>
하고 있으며 종종 그러한 세트에 숫자가 포함되어 있는지 확인해야합니다.
다음과 같이 쓰는 것이 자연 스럽습니다.
if (myset.contains(number))
...
하지만 contains
멤버 가 없어서 귀찮은 글을 써야합니다.
if (myset.find(number) != myset.end())
..
또는 명확하지 않은 경우 :
if (myset.count(element) > 0)
..
이 디자인 결정에 대한 이유가 있습니까?
나는 그들이 만들려고 노력했기 때문에 아마 생각 std::set
과 std::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
'developer tip' 카테고리의 다른 글
파일을 WPF로 끌어서 놓기 (0) | 2020.08.26 |
---|---|
@Transactional (propagation = Propagation.REQUIRED) (0) | 2020.08.26 |
Android 2.0에서 연락처를 읽는 방법 (0) | 2020.08.26 |
수평 확장과 수직 확장은 무엇을 의미합니까? (0) | 2020.08.26 |
알림을 클릭 한 후 애플리케이션 열기 (0) | 2020.08.26 |