사용자 정의 std :: set 비교기 사용


106

정수 집합에서 항목의 기본 순서를 숫자 대신 사전 식으로 변경하려고하는데 다음을 g ++로 컴파일 할 수 없습니다.

file.cpp :

bool lex_compare(const int64_t &a, const int64_t &b) 
{
    stringstream s1,s2;
    s1 << a;
    s2 << b;
    return s1.str() < s2.str();
}

void foo()
{
    set<int64_t, lex_compare> s;
    s.insert(1);
    ...
}

다음과 같은 오류가 발생합니다.

error: type/value mismatch at argument 2 in template parameter list for template<class _Key, class _Compare, class _Alloc> class std::set
error:   expected a type, got lex_compare

내가 뭘 잘못하고 있죠?

답변:


159

functor (함수처럼 호출 될 수 있도록 () 연산자를 오버로드하는 클래스)를 사용해야하는 함수를 사용하고 있습니다.

struct lex_compare {
    bool operator() (const int64_t& lhs, const int64_t& rhs) const {
        stringstream s1, s2;
        s1 << lhs;
        s2 << rhs;
        return s1.str() < s2.str();
    }
};

그런 다음 클래스 이름을 유형 매개 변수로 사용합니다.

set<int64_t, lex_compare> s;

functor 상용구 코드를 피하려면 함수 포인터를 사용할 수도 있습니다 (함수라고 가정 lex_compare).

set<int64_t, bool(*)(const int64_t& lhs, const int64_t& rhs)> s(&lex_compare);

4
@Omry는 : 나는 알고에 관심이있을 것입니다 당신이 사용하고있는 컴파일러 : codepad.org/IprafuVf

1
@Omry 어떤 컴파일러를 사용하고 있습니까?

4
@Omry C ++ 표준은 두 번째 템플릿 매개 변수가 유형의 이름이어야한다고 말합니다. 함수 이름은 유형의 이름이 아닙니다.

6
decltype (lex_compare)을 사용하여 함수 유형을 나타낼 수 있습니까?
Lewis Chan

2
@LewisChan 정확한 용어는 것std::set<int64_t, decltype(&lex_compare)> s(&lex_compare)
Nishant 싱

109

1. 최신 C ++ 20 솔루션

auto cmp = [](int a, int b) { return ... };
std::set<int, decltype(cmp)> s;

우리는 사용 람다 기능을 비교한다. 평소와 같이 비교기는 첫 번째 인수로 전달 된 요소가 정의 된 특정 엄격한 약한 순서 에서 두 번째 인수보다 먼저 전달되는 것으로 간주되는지 여부를 나타내는 부울 값을 반환해야 합니다.

온라인 데모

2. 최신 C ++ 11 솔루션

auto cmp = [](int a, int b) { return ... };
std::set<int, decltype(cmp)> s(cmp);

C ++ 20 이전에는 생성자를 설정하기 위해 람다를 인수로 전달해야합니다.

온라인 데모

3. 첫 번째 솔루션과 유사하지만 람다 대신 함수가 있음

비교기를 일반적인 부울 함수로 만듭니다.

bool cmp(int a, int b) {
    return ...;
}

그런 다음 다음 방법 중 하나로 사용하십시오.

std::set<int, decltype(cmp)*> s(cmp);

온라인 데모

또는 이렇게 :

std::set<int, decltype(&cmp)> s(&cmp);

온라인 데모

4. ()연산자 와 함께 구조체를 사용하는 이전 솔루션

struct cmp {
    bool operator() (int a, int b) const {
        return ...
    }
};

// ...
// later
std::set<int, cmp> s;

온라인 데모

5. 대안 : 부울 함수에서 구조체 생성

부울 함수 가져 오기

bool cmp(int a, int b) {
    return ...;
}

그리고 그것을 사용하여 구조체를 만드십시오. std::integral_constant

#include <type_traits>
using Cmp = std::integral_constant<decltype(&cmp), &cmp>;

마지막으로 구조체를 비교기로 사용

std::set<X, Cmp> set;

온라인 데모


3
예제 1에서 cmp를 생성자에 전달해야합니까? 람다 형식이 템플릿 형식으로 제공되므로 집합 구조 자체가 하나입니까?
PeteUK

2
C ++ 20 비교기 이전의 @PeteUK는 생성자에 전달되어야합니다. C ++ 20에서는 인수없이 생성자를 사용할 수 있습니다. 질문 해 주셔서 감사합니다. 답변이 업데이트되었습니다
diralik

1
@diralik 이미 훌륭한 답변에 대한 답변과 업데이트에 감사드립니다.
PeteUK

1
일반 람다는 1과 2에서도 작동하는 것 같습니다
ZFY

2
그 5. 미쳤어. 매일 새로운 언어 구석 구석을 찾습니다.
Jan Hošek

18

Yacoby의 답변은 functor 상용구를 캡슐화하기위한 어댑터를 작성하도록 영감을줍니다.

template< class T, bool (*comp)( T const &, T const & ) >
class set_funcomp {
    struct ftor {
        bool operator()( T const &l, T const &r )
            { return comp( l, r ); }
    };
public:
    typedef std::set< T, ftor > t;
};

// usage

bool my_comparison( foo const &l, foo const &r );
set_funcomp< foo, my_comparison >::t boo; // just the way you want it!

와우, 그럴만 한 가치가 있다고 생각합니다!


17
의견의 문제라고 생각합니다.

6

다음과 같이 래핑하지 않고 함수 비교기를 사용할 수 있습니다.

bool comparator(const MyType &lhs, const MyType &rhs)
{
    return [...];
}

std::set<MyType, bool(*)(const MyType&, const MyType&)> mySet(&comparator);

이는 해당 유형의 세트가 필요할 때마다 입력하는 것을 짜증나게하며 동일한 비교기로 모든 세트를 생성하지 않으면 문제를 일으킬 수 있습니다.


3

std::less<> 사용자 정의 클래스를 사용할 때 operator<

operator<정의 된 사용자 정의 클래스 집합을 처리하는 경우 std::less<>.

http://en.cppreference.com/w/cpp/container/set/find 에서 언급했듯이 C ++ 14에는 두 가지 새로운 findAPI 가 추가되었습니다 .

template< class K > iterator find( const K& x );
template< class K > const_iterator find( const K& x ) const;

다음을 수행 할 수 있습니다.

main.cpp

#include <cassert>
#include <set>

class Point {
    public:
        // Note that there is _no_ conversion constructor,
        // everything is done at the template level without
        // intermediate object creation.
        //Point(int x) : x(x) {}
        Point(int x, int y) : x(x), y(y) {}
        int x;
        int y;
};
bool operator<(const Point& c, int x) { return c.x < x; }
bool operator<(int x, const Point& c) { return x < c.x; }
bool operator<(const Point& c, const Point& d) {
    return c.x < d;
}

int main() {
    std::set<Point, std::less<>> s;
    s.insert(Point(1, -1));
    s.insert(Point(2, -2));
    s.insert(Point(0,  0));
    s.insert(Point(3, -3));
    assert(s.find(0)->y ==  0);
    assert(s.find(1)->y == -1);
    assert(s.find(2)->y == -2);
    assert(s.find(3)->y == -3);
    // Ignore 1234, find 1.
    assert(s.find(Point(1, 1234))->y == -1);
}

컴파일 및 실행 :

g++ -std=c++14 -Wall -Wextra -pedantic -o main.out main.cpp
./main.out

자세한 정보 std::less<>는 다음에서 찾을 수 있습니다. 투명 비교기는 무엇입니까?

Ubuntu 16.10, g++6.2.0 에서 테스트되었습니다 .

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