Programing

사용자 정의 유형이있는 std :: maps를 키로 사용하려면 어떻게해야합니까?

lottogame 2020. 12. 14. 07:42
반응형

사용자 정의 유형이있는 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 | 오류 : 'operator와 일치하지 않습니다. '__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<실제로, 당신의 클래스. 또한이를위한 비교기 함수 객체 클래스를 만들고이를 사용하여 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입니다 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<클래스 에서 정의해야합니다 .

두 개체는 동등한 것으로 간주 if !(a < b) && !(b < a)됩니다.

어떤 이유로 다른 비교기를 사용하려면의 세 번째 템플릿 인수를 map로 변경할 수 있습니다 ( std::greater예 :).


operator <Class1에 대해 정의해야합니다 .

Map은 연산자 <를 사용하여 값을 비교해야하므로 사용자 정의 클래스를 키로 사용할 때 동일한 값을 제공해야합니다.

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


}

I'd like to expand a little bit on Pavel Minaev's answer, which you should read before reading my answer. Both solutions presented by Pavel won't compile if the member to be compared (such as id in the question's code) is private. In this case, VS2013 throws the following error for me:

error C2248: 'Class1::id' : cannot access private member declared in class 'Class1'

As mentioned by SkyWalker in the comments on Pavel's answer, using a friend declaration helps. If you wonder about the correct syntax, here it is:

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.
};

Code on Ideone

However, if you have an access function for your private member, for example getId() for id, as follows:

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

private:
    int id;
};

then you can use it instead of a friend declaration (i.e. you compare lhs.getId() < rhs.getId()). Since C++11, you can also use a lambda expression for Pavel's first solution instead of defining a comparator function object class. Putting everything together, the code could be writtem as follows:

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

Code on Ideone


The right solution is to Specialize std::less for your class/Struct.

• Basically maps in cpp are implemented as Binary Search Trees.

  1. BSTs compare elements of nodes to determine the organization of the tree.
  2. Nodes who's element compares less than that of the parent node are placed on the left of the parent and nodes whose elements compare greater than the parent nodes element are placed on the right. i.e.

For each node, node.left.key < node.key < node.right.key

Every node in the BST contains Elements and in case of maps its KEY and a value, And keys are supposed to be ordered. More About Map implementation : The Map data Type.

In case of cpp maps , keys are the elements of the nodes and values does not take part in the organization of the tree its just a supplementary data .

So It means keys should be compatible with std::less or operator< so that they can be organized. Please check map parameters.

Else if you are using user defined data type as keys then need to give meaning full comparison semantics for that data type.

Solution : Specialize std::less:

The third parameter in map template is optional and it is std::less which will delegate to operator< ,

So create a new std::less for your user defined data type. Now this new std::less will be picked by std::map by default.

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

}

Note: You need to create specialized std::less for every user defined data type(if you want to use that data type as key for cpp maps).

Bad Solution: Overloading operator< for your user defined data type. This solution will also work but its very bad as operator < will be overloaded universally for your data type/class. which is undesirable in client scenarios.

Please check answer Pavel Minaev's answer

참고URL : https://stackoverflow.com/questions/1102392/how-can-i-use-stdmaps-with-user-defined-types-as-key

반응형