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


103

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

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

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

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

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

또는 명확하지 않은 경우 :

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

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


7
대부분의 표준 라이브러리는 반복자와 함께 작동하므로 일반적으로 반복기를 반환하는 함수는 예상되는 것입니다. 그것을 추상화하는 함수를 작성하는 것은 어렵지 않습니다. 대부분의 경우 컴파일러는 코드 한두 줄이어야하고 동일한 성능을 얻을 수 있기 때문에 인라인 할 것입니다.
NathanOliver

3
count()접근 방식의 또 다른 (더 근본적인) 문제 countains()는해야하는 것 보다 더 많은 작업 을 수행한다는 것입니다.
Leo Heinsaar

11
근본적인 이유 가 디자인 결정 뒤에는 점이다 contains()a가있는 반환 bool요소가 컬렉션에 위치에 대한 유용한 정보를 잃게 . find()반복기의 형태로 해당 정보를 보존하고 반환하므로 STL과 같은 일반 라이브러리에 더 적합합니다. (즉,이 말은 아니다 bool contains().하지만, 매우에 -이 - 좋은 또는 필요하지 않습니다)
레오 Heinsaar

3
contains(set, element)세트의 공용 인터페이스를 사용하여 무료 함수 를 작성하는 것은 쉽습니다 . 따라서 세트의 인터페이스는 기능적으로 완전합니다. 편리한 메서드를 추가하면 추가 기능을 활성화하지 않고 인터페이스가 증가합니다. 이는 C ++ 방식이 아닙니다.
Toby Speight 2017 년

3
요즘 우리는 모든 것을 닫고 있습니까? 이 질문은 어떻게 "주요 의견 기반"입니까?
씨 외국인

답변:


148

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

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

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

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

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


5
덧붙여서,지도와 멀티 맵에서도 똑같습니다 .end().
Matteo Italia

8
또한, 그들은 추가 멤버가 필요을 본 수도 contains()가 있기 때문에 어떤을 위해 중복 될 것이라는 이유로, std::set<T> s그리고 T t, 결과는 s.contains(t)결과와 정확히 동일하다 static_cast<bool>(s.count(t)). 조건식에서 값을 사용하면 암시 적으로로 캐스팅되므로 목적에 충분히 부합 bool한다고 생각했을 수 있습니다 count().
Justin Time-Monica 복원

2
철자가 틀렸나 요? if (myset.ICanHaz(element)) ...: D
Stéphane Gourichon

3
@MartinBonner 그것을 떠나는 이유가 멍청한 것인지는 정말로 중요하지 않습니다. 또한 대화가 100 % 최종 근거가 아니 었는지 여부는 중요하지 않습니다. 여기 당신의 대답은 당신이 생각하는 방법에 단지 합리적인 추측 해야 합니다. 대화와 관련된 누군가의 대답과 그에 관련된 사람의 대답은 (그들은하지 않았더라도) 제안하는 임무를 맡은 것은 당신이 그것을 어떻게 보든이 추측보다 진실에 더 가깝습니다. 최소한 이 답변 에서 적어도 언급 해야합니다. 그것은 큰 개선이 될 것이며 책임감있는 일이 될 것입니다.
Jason C

2
@JasonC : 아래에있는 섹션을 편집 해 주시겠습니까? 나는 당신이 말하려는 요점을 정말로 이해하지 못하며 아마도 의견은 그것을 명확히하는 최선의 방법이 아닐 것입니다. 감사!
마틴 보너 모니카 지원

44

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

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

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

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


5
그리고 그 토론이 그것이 바람직하다는 것과 당신 이 그것에 대한 제안서를 작성하도록 요청 받는 것에 거의 합의로 끝났다는 것을 주목하는 것은 흥미 롭습니다 .
PJTraill

@PJTraill True, 그리고 제가 앞으로 나아 가지 않은 이유는 contains()분명히 기존 컨테이너 및 알고리즘과 강력하게 상호 작용할 것이기 때문입니다. 이는 C ++ 17에 올 것으로 예상되는 시점에 개념과 범위에 의해 큰 영향을받을 것입니다. 나는 (토론과 몇 번의 개인 이메일 교환의 결과로) 그들을 먼저 기다리는 것이 더 나은 생각이라고 확신했습니다. 물론 2015 년에는 개념도 범위도 C ++ 17에 포함되지 않을 것이라는 점이 분명하지 않았습니다 (사실 그럴 것이라는 기대가 높았습니다). 그래도 지금 그것을 추구 할 가치가 있는지 모르겠습니다.
Leo Heinsaar 2017 년

1
들어 std::set(질문에 대해 묻는 것입니다), I는 표시되지 않습니다 count보다 더 많은 작업을 수행 contains해야 할 것가. 의 glibc 구현 count은 (대략) return find(value) == end() ? 0 : 1;입니다. 삼항 연산자와 반환 != end()(최적화 프로그램이 제거 할 것으로 예상 함) 에 대한 세부 정보를 제외하고 는 더 이상 작업이 있는지 알 수 없습니다.
마틴 보너 모니카 지원

4
"... 부울을 반환하는 contains ()는 요소가 컬렉션에서 어디에 있는지에 대한 중요한 정보를 잃을 것입니다. "-사용자가 호출 myset.contains()(존재하는 경우)하면 해당 정보가 가치가 없음을 나타내는 매우 강력한 표시입니다 ( 해당 컨텍스트에서 사용자에게).
Keith Thompson

1
count()해야 할 일보다 더 많은 일 contains()std::set합니까? 그것은 독특하므로 정확히 똑같은 count()것일 수 있습니다 return contains(x) ? 1 : 0;.
Timmmm

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)) {
}

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

이렇게하는 것이 훨씬 더 합리적입니다.

if (some_set.count(some_element)) {
}

그러나 나는 확장 방법 방법에 재미 있습니다.

정말 슬픈 것은 효율적인를 작성하는 것입니다 contains빠른 속도에 수 multimap또는 multiset동안 그들은 단지 하나 개의 요소를 찾아 가지고, count그들 각각을 찾아야 하고이를 계산합니다 .

10 억 개의 7 복사본을 포함하는 멀티 세트 (모두 다 떨어질 경우를 대비하여)는 매우 느릴 .count(7)수 있지만 매우 빠를 수 있습니다 contains(7).

위의 확장 방법을 사용 lower_bound하면를 사용 end하고를 비교 한 다음 요소와 비교 하여이 경우를 더 빠르게 만들 수 있습니다 . 그러나 순서가 지정되지 않은 meow뿐만 아니라 순서가 지정된 meow에 대해 그렇게하려면 멋진 SFINAE 또는 컨테이너 특정 과부하가 필요합니다.


2
7 개 10 억 개? 그리고 여기서는 std::set중복을 포함 할 수 없으므로 std::set::count항상 0또는을 반환 할 것이라고 생각했습니다 1.
NWP

5
@nwp std::multiset::countcan
milleniumbug

2
@nwp backticks"세트"라는 단어가 부족한 이유는 std::set구체적으로 언급하지 않았기 때문 입니다. 당신이 기분을 좋게하기 위해, 나는 멀티 추가 할 것입니다
Yakk - 아담 Nevraumont

3
나는 "야옹"이 무엇을 언급해야하는지에 대한 농담을 놓치고있는 것 같다.
user2357112 모니카 지원

2
@ user2357112 meow는 "set or map"의 자리 표시 자입니다. STL에 이유를 물어보십시오 .
Yakk-Adam Nevraumont

12

특정 사례를 살펴보고 있으며 더 큰 그림을 보지 못하고 있습니다. 문서에 명시된 바와 같이 AssociativeContainer 개념의 std::set요구 사항을 충족합니다 . 이 개념의 경우 및 에 대해서는 거의 쓸모가 없지만 모든 방법에 대해 잘 작동 하므로 방법 을 갖는 것은 의미가 없습니다 . 방법은 있지만 에 대한 별칭으로 추가 할 수 있습니다 에 대한 , 자신의 해시 버전 (같은 대한 의 ), 그러나 라이브러리 제작자와 같은 모습은 실질 필요성을하지 않았다.containsstd::multisetstd::multimapcountcontainscountstd::setstd::maplengthsize()std::string


8
string괴물 이라는 점 에 유의 하십시오. STL 이전에 존재했으며 length인덱스 기반의 모든 메소드를 보유한 다음 이전 버전과의 호환성을 위해 기존 메소드를 제거하지 않고 STL 모델에 맞도록 "컨테이너화"되었습니다. . GotW # 84 참조 : Monoliths Unstrung => string"최소한의 멤버 함수"설계 원칙을 위반합니다.
Matthieu M.

5
그러나 질문은 "왜 그런 AssociativeContainer 개념을 가질 가치가 있는가?"가됩니다. -그리고 그게 뒤늦은 지 모르겠습니다.
Martin Bonner가 Monica 지원

24
멀티 세트, 멀티 맵 또는지도에 무언가가 포함되어 있는지 묻는 것이 나에게 완벽하게 이해됩니다. 사실, contains세트 / 맵에 대한 노력은 동일하지만 다중 세트 / 멀티 맵 보다 빠르게 만들 수 있습니다 count.
Yakk-Adam Nevraumont

5
에 AssociativeContainer의 수업을 필요로하지 않습니다 하지contains방법을.
user2357112 모니카 지원

6
@Slava 말처럼 있다고 size()empty()중복은 아직 많은 용기가 모두 있습니다.
Barry

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

다음과 같이 사용하십시오.

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

3
-1, 이것은 contains메서드가 실제로 존재 한다는 사실과 직접적으로 모순되기 때문에 어리석은 방식으로 명명되었습니다.
Matteo Italia

4
는 "STL의 최선의 노력을 기울이고은 최소한의 인터페이스를 제공하는" 붉은 부리 까마귀 std::string 기침
bolov

6
@bolov : 요점? std.::stringSTL의 일부가 아닙니다! 표준 라이브러리의 일부이며 소급 템플릿이 적용되었습니다 ...
MFH

3
@MatteoItalia count는 코드가 .NET find과 공유되는 경우 범위의 시작과 끝을 얻기 위해 효과적으로 두 s 를 수행해야하기 때문에 더 느릴 수 있습니다 multiset.
Mark Ransom

2
OP는 이미 중복된다는 것을 알고 있지만 분명히 코드가 contains. 나는 그것에 대해 잘못된 것이 없습니다. @MarkRansom 작은 SFINAE는이 템플릿이해서는 안되는 것에 바인딩하는 것을 방지하는 것입니다.
rustyx

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

물론이 모든 것은 단순한 추측 일뿐입니다.


1
예, 그러나 int 집합의 경우 적용되지 않습니다.
Jabberwocky

7
사람들은 그냥 if (myMap.count("Meaning of universe"))잘 쓸 수 있다는 점을 제외하고는 ...?
Barry

@MichaelWalz 죄송합니다. 맞아요. 나는 세트 예제도 포함하도록 대답을 수정했습니다. 그러나 int 집합에 대한 추론은 나에게 미스터리입니다.
Martin Drozdik

2
이것은 옳을 수 없습니다. .NET과 contains마찬가지로 비효율적 인 코드를 쉽게 작성할 수 있습니다 count.
마틴 보너 모니카 지원

2

C ++ 20부터

bool contains( const Key& key ) const

사용할 수 있습니다.


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;

에서는 작동 std::unordered_set하지 않지만에서는 작동합니다 std::set.
Jabberwocky

이는 정상이며 binary_search는 이진 트리에서만 작동합니다.
Massimiliano Di Cavio

0

contains ()는 부울을 반환해야합니다. C ++ 20 컴파일러를 사용하여 코드에 대해 다음 출력을 얻습니다.

#include<iostream>
#include<map>
using namespace std;

int main()
{
    multimap<char,int>mulmap;
    mulmap.insert(make_pair('a', 1)); //multiple similar key
    mulmap.insert(make_pair('a', 2)); //multiple similar key
    mulmap.insert(make_pair('a', 3)); //multiple similar key
    mulmap.insert(make_pair('b', 3));
    mulmap.insert({'a',4});
    mulmap.insert(pair<char,int>('a', 4));
    
    cout<<mulmap.contains('c')<<endl;  //Output:0 as it doesn't exist
    cout<<mulmap.contains('b')<<endl;  //Output:1 as it exist
}

-1

또 다른 이유는 프로그래머에게 std :: set이 수학 집합 이론의 집합이라는 잘못된 인상을주기 때문입니다. 그들이 그것을 구현하면 다른 많은 질문이 뒤따를 것입니다. std :: set에 값에 대해 contains ()가 있다면 왜 다른 세트에 대해 가지고 있지 않습니까? Union (), Intersection () 및 기타 집합 연산과 술어는 어디에 있습니까?

물론 대답은 일부 집합 연산이 이미 (std :: set_union () 등)의 함수로 구현되어 있고 다른 일부는 contains ()처럼 사소하게 구현된다는 것입니다. 함수 및 함수 개체는 개체 멤버보다 수학 추상화에서 더 잘 작동하며 특정 컨테이너 유형으로 제한되지 않습니다.

전체 수학 집합 기능을 구현해야하는 경우 기본 컨테이너를 선택할 수있을뿐만 아니라 구현 세부 사항도 선택할 수 있습니다. 예를 들어, theory_union () 함수가 함수 프로그래밍에 더 적합한 불변 객체와 함께 작동할까요? , 아니면 피연산자를 수정하고 메모리를 절약합니까? 처음부터 함수 객체로 구현 될까요, 아니면 C 함수를 구현하고 필요한 경우 std :: function <>을 사용하는 것이 더 낫습니까?

지금처럼 std :: set은 단지 컨테이너 일 뿐이며 수학적인 의미에서 set의 구현에 적합하지만 이론적 벡터에서 std :: vector만큼 이론적 집합이 아닙니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.