Подтвердить что ты не робот

Как я могу использовать std::maps с пользовательскими типами в качестве ключа?

Мне интересно, почему я не могу использовать карты STL с пользовательскими классами. Когда я компилирую код ниже, я получаю следующее загадочное сообщение об ошибке. Что это значит? Кроме того, почему это происходит только с пользовательскими типами? (Примитивные типы в порядке, когда они используются в качестве ключа.)

C:\MinGW\Bin..\Lib\НКУ\mingw32\3.4.5........\Include\c++\3.4.5\бит\stl_function.h || В функция-член 'bool std::less & lt; _Tp> :: operator() (const _Tp & amp;, const _Tp & amp;) const [с _Tp = Class1]: |

C:\MinGW\bin..\lib\gcc\mingw32\3.4.5........\include\c++\3.4.5\bits\stl_map.h|338|instantiated from '_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|instantiated from here|

C:\MinGW\bin..\lib\gcc\mingw32\3.4.5........\include\c++\3.4.5\bits\stl_function.h|227|error: no match for 'operator<' in '__x < __y'| ||=== Build finished: 1 errors, 0 warnings ===|

#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;
}
4b9b3361

Ответ 1

На самом деле вам не нужно определять 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< клиентскому коду в противном случае.

Ответ 2

По умолчанию std::mapstd::set) используют operator< для определения сортировки. Поэтому вам нужно определить operator< для вашего класса.

Два объекта считаются эквивалентными if !(a < b) && !(b < a).

Если по какой-либо причине вы хотите использовать другой компаратор, третий аргумент шаблона map можно изменить, например, на std::greater.

Ответ 3

Вам нужно определить operator < для Class1.

Карта должна сравнивать значения с помощью оператора < и, следовательно, вам нужно предоставить то же самое, когда пользовательский класс используется как ключ.

class Class1
{
public:
    Class1(int id);

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

Ответ 4

Ключи должны быть сопоставимыми, но вы не определили подходящий operator< для своего пользовательского класса.

Ответ 5

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


}

Ответ 6

Я хотел бы немного подробнее рассказать об ответе Павла Минаева answer, который вы должны прочитать, прежде чем читать мой ответ. Оба решения, представленные Павлом, не будут компилироваться, если сравниваемый элемент (например, id в коде вопроса) является закрытым. В этом случае VS2013 выдает мне следующую ошибку:

ошибка C2248: 'Class1 :: id': невозможно получить доступ к закрытому члену, объявленному в классе 'Class1'

Как упомянуто SkyWalker в комментариях к ответу Павла, помогает использование декларации friend. Если вас интересует правильный синтаксис, вот он:

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

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

Код на Ideone

Однако, если у вас есть функция доступа для вашего частного участника, например getId() для id, как показано ниже:

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

private:
    int id;
};

тогда вы можете использовать его вместо декларации friend (т.е. вы сравниваете lhs.getId() < rhs.getId()). Начиная с С++ 11, вы также можете использовать лямбда-выражение для первого решения Павла вместо определения класса объекта функции компаратора. Собрав все вместе, код можно написать следующим образом:

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

Код на Ideone

Ответ 7

Правильное решение - специализировать std::less для вашего класса/структуры.

• В основном карты в cpp реализованы как деревья бинарного поиска.

  1. BST сравнивают элементы узлов, чтобы определить организацию дерева.
  2. Узлы, элементы которых сравниваются меньше, чем у родительского узла, размещаются слева от родительского узла, а узлы, элементы которых сравниваются больше, чем элемент родительских узлов, располагаются справа. т.е.

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

Каждый узел в BST содержит элементы, а в случае отображения его KEY и значение, а ключи А должны быть упорядочены. Подробнее о реализации карты: Тип данных карты.

В случае карт cpp ключи являются элементами узлов, а значения не участвуют в организации дерева, это просто дополнительные данные.

Это означает, что ключи должны быть совместимы с std::less или operator<, чтобы их можно было организовать. Пожалуйста, проверьте параметры карты.

Иначе, если вы используете пользовательский тип данных в качестве ключей, необходимо указать полную семантику сравнения для этого типа данных.

Решение: специализировать std::less:

Третий параметр в шаблоне карты является необязательным, и это 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< для вашего пользовательского типа данных. Это решение также будет работать, но его очень плохо, поскольку оператор < будет перегружен повсеместно для вашего типа данных/класса. что нежелательно в клиентских сценариях.

Пожалуйста, проверьте ответ Павел Минаев ответ