C ++ Абстрактный класс как ключ std :: map - PullRequest
7 голосов
/ 25 апреля 2011

У меня есть иерархия классов, подобная этой:

struct Vehicle {
  virtual string model() = 0; // abstract
  ...
}
struct Car : public Vehicle {...}
struct Truck : public Vehicle {...}

Мне нужно сохранить std::map с некоторой информацией, которую я получаю о некоторых Vehicle экземплярах:

std::map<Vehicle, double> prices;

Однако я получаю следующую ошибку:

/usr/include/c++/4.2.1/bits/stl_pair.h: In instantiation of ‘std::pair<const Vehicle, double>’:
/usr/include/c++/4.2.1/bits/stl_map.h:349:   instantiated from ‘_Tp& std::map<_Key, _Tp, _Compare, _Alloc>::operator[](const _Key&) [with _Key = Vehicle, _Tp = double, _Compare = std::less<Vehicle>, _Alloc = std::allocator<std::pair<const Vehicle, double> >]’
test.cpp:10:   instantiated from here
/usr/include/c++/4.2.1/bits/stl_pair.h:73: error: cannot declare field ‘std::pair<const Vehicle, double>::first’ to be of abstract type ‘const Vehicle’
Model.hpp:28: note:   because the following virtual functions are pure within ‘const Vehicle’:
Model.hpp:32: note:     virtual string Vehicle::model()

Так что вы не можете использовать абстрактный класс в качестве ключа std::map.Насколько я могу судить, это потому, что карты копируют свои ключи (через конструктор копирования или оператор присваивания), и это подразумевает создание абстрактного класса (Vehicle).Кроме того, даже если бы вы могли, мы все равно стали бы жертвой нарезки объектов .

Что мне делать?

Кажется, я не могу использовать указатели потому что могут быть отдельные копии логически идентичных Car с или Truck с.(т.е. два Car объекта создаются отдельно, но которые представляют одну и ту же машину, и operator== возвращает true. Мне нужно, чтобы они отображались на один и тот же объект в std::map.)

Ответы [ 3 ]

10 голосов
/ 25 апреля 2011
  1. Вам нужно использовать указатели на Vehicle.

  2. operator== используется не std::map, а функтором сравнения, который является третьим параметром std::map. По умолчанию это std::less. Вам нужно реализовать свой собственный функтор сравнения для работы с Vehicle:

     struct less_vehicle: std::binary_function<const Vehicle *, const Vehicle *, bool>
     {
       bool  operator() (const Vehicle *a, const Vehicle *b) const { ... }
     };
    

А затем используйте его:

std::map<Vehicle *, double, less_vehicle>
1 голос
/ 25 апреля 2011

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

С линией std::map<Vehicle, double> prices;, которую вы говорите, "создайте карту с Vehicle экземпляров в качестве ключей ".Поскольку у вас не может быть экземпляров Vehicle (поскольку это чисто виртуальный), вы также не можете иметь их в качестве ключей для вашей карты.

Я не баловался с этим раньше, но яповерьте, используя собственный распределитель с map, вы можете использовать Vehicle указатели с проверками для логически идентичных указателей.

1 голос
/ 25 апреля 2011

Вам нужно будет использовать указатели и передать аргумент шаблона, чтобы указать тип объекта, который будет выполнять сравнение (а когда вы создаете карту, передайте объект этого типа для сравнения).

Для сравнения вы захотите, чтобы разыменовывались указатели и сравнивались объекты, на которые они указывают. Также обратите внимание, что для этого вам нужно определить какое-то сравнение между автомобилем и грузовиком. Оно не обязательно должно быть особенно значимым, но оно должно быть последовательным и транзитивным (официальная терминология заключается в том, что оно должно определять «строгое слабое упорядочение»).

...