Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++ Abstract Class as std::map key

I have a class hierarchy like this:

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

I need to keep a std::map with some information that I acquire about some Vehicle instances:

std::map<Vehicle, double> prices;

However I get the following error:

/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()

So you can't use an abstract class as a std::map key. As far as I can tell this is because maps copy their keys (via copy-constructor or assignment operator) and this would imply instantiating an abstract class (Vehicle). Also even if you could, we'd fall prey to object slicing anyway.

What should I do?

It seems I can't use pointers because there might be separate copies of logically identical Cars or Trucks. (i.e. Two Car objects instantiated separately, but which represent the same car and operator== returns true. I need these to map to the same object in the std::map.)

like image 361
Adam Ernst Avatar asked Apr 25 '11 05:04

Adam Ernst


1 Answers

  1. You need to use pointers to Vehicle.

  2. operator== is not used by the std::map but a compare functor which is the third parameter of the std::map. By default it is std::less. You need to implement your own compare functor to work with Vehicle:

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

And then use it:

std::map<Vehicle *, double, less_vehicle>
like image 185
Begemoth Avatar answered Oct 01 '22 13:10

Begemoth