ユーザー定義のクラスで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;
}
実際には、クラスの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<
をクライアントコードに公開しないことです。
デフォルトでは _std::map
_ (および _std::set
_ ) _operator<
_ を使用してソートを決定します。したがって、クラスで_operator<
_を定義する必要があります。
2つのオブジェクトは、 同等if !(a < b) && !(b < a)
と見なされます。
何らかの理由で別のコンパレータを使用する場合は、map
の3番目のテンプレート引数を、たとえば _std::greater
_ に変更できます。
Class1にoperator <
を定義する必要があります。
マップでは、演算子<を使用して値を比較する必要があるため、ユーザー定義クラスをキーとして使用する場合は同じ値を指定する必要があります。
class Class1
{
public:
Class1(int id);
bool operator <(const Class1& rhs) const
{
return id < rhs.id;
}
private:
int id;
};
キーは比較可能でなければなりませんが、カスタムクラスに適したoperator<
を定義していません。
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;
}
}
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.
};
ただし、次のように、id
のgetId()
など、プライベートメンバーのアクセス関数がある場合:
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);
適切な解決策は、クラス/構造にstd::less
を特化することです。
•基本的にcppのマップは、バイナリ検索ツリーとして実装されます。
各ノードについて、node.left.key <node.key <node.right.key
BSTのすべてのノードには要素が含まれており、マップの場合、そのキーと値、およびキーは順序付けられます。マップの実装の詳細: マップデータタイプ 。
Cppマップの場合、キーはノードの要素であり、値は単なる補足データであるツリーの組織に参加しません。
したがって、キーはstd::less
またはoperator<
と互換性があり、それらを整理できる必要があります。 マップパラメーター を確認してください。
それ以外の場合、ユーザー定義のデータ型をキーとして使用している場合、そのデータ型に対して完全な比較セマンティクスを意味する必要があります。
Solution:std::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の答え