web-dev-qa-db-ja.com

ユーザー定義型をキーとしてstd :: mapsを使用するにはどうすればよいですか?

ユーザー定義のクラスでSTLマップを使用できないのはなぜかと思っています。以下のコードをコンパイルすると、次の不可解なエラーメッセージが表示されます。どういう意味ですか?また、なぜユーザー定義型でのみ発生するのですか? (プリミティブ型は、キーとして使用される場合は問題ありません。)

C:\ MinGW\bin ..\lib\gcc\mingw32\3.4.5 ........\include\c ++\3.4.5\bits\stl_function.h ||メンバー関数で「bool std :: less <_Tp> :: operator()(const _Tp&、const _Tp&)const [with _Tp = Class1] ':|

C:\ MinGW\bin ..\lib\gcc\mingw32\3.4.5 ........\include\c ++\3.4.5\bits\stl_map.h | 338 | `_Tp&std ::からインスタンス化map <_Key、_Tp、_Compare、_Alloc> :: operator [](const _Key&)[with _Key = Class1、_Tp = int、_Compare = std :: less、_Alloc = std :: allocator>] '|

C:\ Users\Admin\Documents\dev\sandbox\sandbox\sandbox.cpp | 24 |ここからインスタンス化|

C:\ MinGW\bin ..\lib\gcc\mingw32\3.4.5 ........\include\c ++\3.4.5\bits\stl_function.h | 227 |エラー:「演算子」に一致しません'__x <__y' |の<' || ===ビルドが完了しました:1エラー、0警告=== |

#include <iostream>
#include <map>

using namespace std;

class Class1
{
public:
    Class1(int id);

private:
    int id;
};

Class1::Class1(int id): id(id)
{}

int main()
{
    Class1 c1(1);

    map< Class1 , int> c2int;
    c2int[c1] = 12;

    return 0;
}
55
unknown

実際には、クラスのoperator<を定義する(haveしません)コンパレータ関数オブジェクトクラスを作成して、std::mapを特殊化することもできます。あなたの例を拡張するには:

struct Class1Compare
{
   bool operator() (const Class1& lhs, const Class1& rhs) const
   {
       return lhs.id < rhs.id;
   }
};

std::map<Class1, int, Class1Compare> c2int;

std::mapの3番目のテンプレートパラメータのデフォルトは std::less であり、これはクラスに定義されたoperator<に委任されます(存在する場合は失敗します)なし)。ただし、オブジェクトをマップキーとして使用したい場合がありますが、実際には意味のある比較セマンティクスがないため、混同したくない場合があります。そのためにあなたのクラスでoperator<を提供することによって人々。その場合は、上記のトリックを使用できます。

同じことを実現するさらに別の方法は、std::lessを専門化することです。

namespace std
{
    template<> struct less<Class1>
    {
       bool operator() (const Class1& lhs, const Class1& rhs) const
       {
           return lhs.id < rhs.id;
       }
    };
}

これの利点は、「デフォルトで」std::mapによって選択され、それ以外の場合はoperator<をクライアントコードに公開しないことです。

127
Pavel Minaev

デフォルトでは _std::map_ (および _std::set__operator<_ を使用してソートを決定します。したがって、クラスで_operator<_を定義する必要があります。

2つのオブジェクトは、 同等if !(a < b) && !(b < a)と見なされます。

何らかの理由で別のコンパレータを使用する場合は、mapの3番目のテンプレート引数を、たとえば _std::greater_ に変更できます。

22
GManNickG

Class1にoperator <を定義する必要があります。

マップでは、演算子<を使用して値を比較する必要があるため、ユーザー定義クラスをキーとして使用する場合は同じ値を指定する必要があります。

class Class1
{
public:
    Class1(int id);

    bool operator <(const Class1& rhs) const
    {
        return id < rhs.id;
    }
private:
    int id;
};
13
aJ.

キーは比較可能でなければなりませんが、カスタムクラスに適したoperator<を定義していません。

3
unwind
class key
{
    int m_value;
public:
    bool operator<(const key& src)const
    {
        return (this->m_value < src.m_value);
    }

};
int main()
{
    key key1;
    key key2;
    map<key,int> mymap;
    mymap.insert(pair<key,int>(key1,100));
    mymap.insert(pair<key,int>(key2,200));
    map<key,int>::iterator iter=mymap.begin();
    for(;iter!=mymap.end();++iter)
    {
        cout<<iter->second<<endl;
    }


}
3
Kaushal

Pavel Minaev's answer を少し拡張したいので、答えを読む前に読む必要があります。比較するメンバー(質問のコードのidなど)がプライベートの場合、Pavelが提示する両方のソリューションはコンパイルされません。この場合、VS2013は次のエラーをスローします。

エラーC2248: 'Class1 :: id':クラス 'Class1'で宣言されたプライベートメンバーにアクセスできません

Pavel's answerの comments SkyWalker で述べたように、friendを使用して宣言が役立ちます。正しい構文について疑問がある場合は、次のとおりです。

class Class1
{
public:
    Class1(int id) : id(id) {}

private:
    int id;
    friend struct Class1Compare;      // Use this for Pavel's first solution.
    friend struct std::less<Class1>;  // Use this for Pavel's second solution.
};

イデオンのコード

ただし、次のように、idgetId()など、プライベートメンバーのアクセス関数がある場合:

class Class1
{
public:
    Class1(int id) : id(id) {}
    int getId() const { return id; }

private:
    int id;
};

friend宣言の代わりに使用できます(つまり、lhs.getId() < rhs.getId()を比較します)。 C++ 11 なので、 Pavel's の代わりに lambda式 を使用することもできますコンパレーター関数オブジェクトクラスの定義。すべてをまとめると、コードは次のように書かれます。

auto comp = [](const Class1& lhs, const Class1& rhs){ return lhs.getId() < rhs.getId(); };
std::map<Class1, int, decltype(comp)> c2int(comp);

イデオンのコード

1
honk

適切な解決策は、クラス/構造にstd::lessを特化することです。

•基本的にcppのマップは、バイナリ検索ツリーとして実装されます。

  1. BSTはノードの要素を比較して、ツリーの構成を決定します。
  2. 親ノードの要素よりも小さい要素を比較するノードは親の左側に配置され、親ノード要素よりも大きい要素を比較するノードは右側に配置されます。つまり.

各ノードについて、node.left.key <node.key <node.right.key

BSTのすべてのノードには要素が含まれており、マップの場合、そのキーと値、およびキーは順序付けられます。マップの実装の詳細: マップデータタイプ

Cppマップの場合、キーはノードの要素であり、値は単なる補足データであるツリーの組織に参加しません。

したがって、キーはstd::lessまたはoperator<と互換性があり、それらを整理できる必要があります。 マップパラメーター を確認してください。

それ以外の場合、ユーザー定義のデータ型をキーとして使用している場合、そのデータ型に対して完全な比較セマンティクスを意味する必要があります。

Solutionstd::lessを特殊化::

マップテンプレートの3番目のパラメーターはオプションであり、std::lessであり、operator<に委任されます。

そのため、ユーザー定義のデータ型用に新しいstd::lessを作成します。これで、この新しいstd::lessはデフォルトでstd::mapによって選択されます。

namespace std
{
    template<> struct  less<MyClass>
    {
        bool operator() (const MyClass& lhs, const MyClass& rhs) const
        {
            return lhs.anyMemen < rhs.age;
        }
    };

}

注:ユーザー定義のデータ型ごとに特別なstd::lessを作成する必要があります(そのデータ型をcppマップのキーとして使用する場合)。

悪い解決策:ユーザー定義のデータ型のoperator<をオーバーロードしています。このソリューションも機能しますが、演算子<がデータ型/クラスに対して普遍的にオーバーロードされるため、非常に悪いです。クライアントシナリオでは望ましくありません。

答えを確認してください Pavel Minaevの答え

0
BreakBadSP