カスタムstd :: setコンパレーターの使用


105

整数のセット内のアイテムのデフォルトの順序を数値ではなく辞書式に変更しようとしています。次のものを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

私は何を間違っていますか?

回答:


158

ファンクタ(()演算子をオーバーロードして関数のように呼び出すことができるクラス)を使用する必要がある場所で関数を使用しています。

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;

ファンクターのボイラープレートコードを避けたい場合は、関数ポインターを使用することもできます(関数と仮定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 ++標準では、2番目のテンプレートパラメータは型の名前である必要があります-関数名は型の名前ではありません。

6
関数型を示すためにdecltype(lex_compare)を使用できますか?
ルイスチャン

2
@LewisChanの正しい用語は次のようになりますstd::set<int64_t, decltype(&lex_compare)> s(&lex_compare)
Nishant Singh

109

1.最新のC ++ 20ソリューション

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

ラムダ関数をコンパレータとして使用します。いつものように、コンパレータはブール値を返す必要があります。これは、最初の引数として渡された要素が、それが定義する特定の厳密な弱い順序付けで2番目の引数の前にあると見なされるかどうかを示します。

オンラインデモ

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を使用した古いソリューション

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.は異常です。毎日、言語の新しい隅々を見つけます。
JanHošek

18

Yacobyの答えは、ファンクターのボイラープレートをカプセル化するためのアダプターを作成するように私に刺激を与えます。

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には2つの新しい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 g++16.10、6.2.0でテスト済み。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.