Как я могу использовать 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 [с _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 &) [с _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 | ошибка: нет соответствия для оператора ' ‹'In' __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;
}

person unknown    schedule 09.07.2009    source источник
comment
Возможный дубликат C ++ unordered_map с использованием настраиваемого типа класса как ключ   -  person Ciro Santilli 新疆再教育营六四事件ۍ    schedule 18.04.2019


Ответы (7)


На самом деле вы не должны определять 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 установлено значение _5 _, который будет делегирован 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< клиентскому коду.

person Pavel Minaev    schedule 09.07.2009
comment
Я бы предложил добавить ключевое слово const к двум функциям. - person Diomidis Spinellis; 28.08.2010
comment
возможно, стоит friend использовать struct less, иначе я считаю это скомпрометированной инкапсуляцией. - person SkyWalker; 03.10.2012
comment
Шаблонная структура должна заканчиваться точкой с запятой, иначе вы получите ошибки компиляции. К сожалению, я не смог исправить это с помощью редактирования из-за небольшого количества измененных символов. - person Ident; 27.07.2015
comment
Но почему вы помещаете struct less info std? - person Vladimir Tsyshnatiy; 18.07.2016
comment
Это уже в std. Это просто его специализация. - person Pavel Minaev; 18.07.2016

По умолчанию std::map_ 2_) используйте operator< для определения сортировки. Следовательно, вам необходимо определить operator< в своем классе.

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

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

person GManNickG    schedule 09.07.2009
comment
Фактически, вы можете изменить компаратор на любую функцию с двумя аргументами. - person xtofl; 09.07.2009

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

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

class Class1
{
public:
    Class1(int id);

    bool operator <(const Class1& rhs) const
    {
        return id < rhs.id;
    }
private:
    int id;
};
person aJ.    schedule 09.07.2009
comment
Оператор ‹не нужен; это просто по умолчанию. См. Ответ GMan или Павла. - person xtofl; 09.07.2009

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

person unwind    schedule 09.07.2009

Я хотел бы немного расширить ответ Павла Минаева, который вам следует прочитать перед читая мой ответ. Оба решения, представленные Павлом, не будут компилироваться, если сравниваемый член (например, 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's first solution.
    friend struct std::less<Class1>;  // Use this for Pavel's 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()). Начиная с C ++ 11, вы также можете использовать лямбда-выражение для первого решения Павла вместо определения класса объекта функции компаратора . Собрав все вместе, код можно было бы написать следующим образом:

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

Код на Ideone

person honk    schedule 06.03.2019

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

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

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

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

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

person BreakBadSP    schedule 24.08.2018

person    schedule
comment
Добро пожаловать в StackOverflow! Пожалуйста, добавьте пояснения к своему ответу. - person Aurasphere; 06.11.2015