unordered_map и виртуальный шаблон - PullRequest
1 голос
/ 07 июня 2011

Проблема заключается в следующем: я хочу использовать unordered_map для хранения ключей и значений, где ключ может быть либо класса A, либо класса B, в зависимости от пользовательских настроек. Оба класса A и B наследуются от одного и того же класса P.

class A: public P {...}
class B: public P {...}

Я хотел бы определить карту с помощью абстрактного класса P, а затем, в зависимости от параметров времени выполнения, назначить карту с ключом A или B в качестве ключа:

unordered_map< P, CValue, P::hash, P::equal_to> * pmap = new unordered_map< A, CValue, A::hash, A::equal_to>;

но я получу ошибку:

 cannot convert ... in initialization

Как я могу объявить такую ​​"виртуальную" карту?

Ответы [ 2 ]

3 голосов
/ 08 июня 2011

Вот пример того, как вы можете сделать карту ключом P*, но при этом использовать различные реализации в производных классах:

struct P
{
  virtual size_t hash_self() const = 0;
  virtual bool equal(const P &) const = 0;
};

struct A : public P
{
  inline bool operator==(const A & other) const { return false; /*Implement!*/}
  size_t hash_self() const { return 1; /*Implement!*/ }
  bool equal(const P & p) const { return *this == dynamic_cast<const A &>(p); }
};

struct PHash
{
  size_t operator()(const P * const p) const { return p->hash_self(); }
};

struct PEqual
{
  bool operator()(const P * const p, const P * const q) const { return p->equal(*q); }
};


#include <unordered_map>

std::unordered_map<P *, double, PHash, PEqual> pmap{{ new A, .5 }};

Динамическое приведение действует, потому что вы обещаете сравнивать только указатели одного и того же производного типа.

Если вы хотите быть чище, вы, вероятно, можете специализироваться std::hash<P*> и std::equal_to<P*>:

namespace std
{
  template<> struct hash<P*>
  { size_t operator()(P * const & p) const { return p->hash_self(); } };

  template<> struct equal_to<P*> : public binary_function<P*, P*, bool>
  { bool operator()(P * const & p, P * const & q) const { return p->equal(*q); } };
}

std::unordered_map<P *, int> qmap{{new A, -11}}; // just works!
2 голосов
/ 07 июня 2011
unsorted_map< P, CValue, P::hash, P::equal_to> * pmap = new unsorted_map< A, CValue, A::hash, A::equal_to>;

Тип P отличается от типа A.

То есть X<P> отличается от X<A>. Это означает, что этот код

X<P> *pX = new X<A>();

не будет компилироваться, даже если A получено из P. GCC выдаст эту ошибку ( ideone ):

error: cannot convert ‘X<A>*’ to ‘X<P>*’ in initialization

, что самоочевидно, если вы знаете, что X<A> - это совершенно другой тип, чем X<P>

Обратите внимание, что это A, которое получено из P. Но X<A> все еще НЕ является производным от X<P>. Я думаю, что вы путаете последнее с первым.

Так что я думаю, что вам нужно это:

unorder_map<P*, P::hash, P::equal_to>  objectMap;

Вы можете вставить объект типа A* в эту карту:

objectMap.insert(new A());

Вы также можете вставить объект типа B*:

objectMap.insert(new B());

В конце концов, вы хотите обрабатывать все объекты на карте полиморфно.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...