Я пишу библиотеку на C ++, для которой я реализовал обертку карты с типом стирания. Оболочка структурирована как в этой замечательной статье: http://www.cplusplus.com/forum/articles/18756/.
TLDR:
template<typename K, typename V>
class AnyMap
{
class MapConcept
{
public:
// Lots of pure virtual methods...
// These mimic the intersection of STL and Boost maps' interfaces
// Example:
virtual size_type size() = 0;
};
template<typename ActualMapType>
class MapModel : MapConcept
{
ActualMapType m;
public:
// Implementations of the parent's virtual methods...
// These basically just call the same method on member m.
// Example:
size_type size() { return m.size(); }
};
MapConcept* mapConcept;
public:
// Again, the STL and Boost maps' interface methods
// Example:
size_type size() { return mapConcept->size(); }
};
Я не уверен, представлю ли я эту карту как часть готовой библиотеки или уберу ее как вспомогательный класс, но в любом случае мне интересно, что делать с оператором присваивания.
В настоящее время у меня есть что-то вроде этого:
AnyMap& AnyMap::operator=(const AnyMap& other) {
delete mapConcept;
mapConcept = other.mapConcept->clone();
return *this;
}
Это означает, что если я создам две карты, скажем, карту STL и карту unordered_map в Boost, а затем назначу одну для другой, то теперь обе карты будут иметь один и тот же тип карты.
std::map<string, int> stlMap;
boost::unordered_map<string, int> boostMap;
// insert some stuff into maps
AnyMap<string, int> stlAnyMap( stlMap );
AnyMap<string, int> boostAnyMap( boostMap );
stlAnyMap = boostAnyMap;
// now stlAnyMap has a copy of boostMap
Таким образом, это имеет некоторый смысл, потому что содержание карты, назначенной для, является тем, что ожидается. Однако я подозреваю, что обычно типы карт будут отличаться одним из аргументов типа со значениями по умолчанию (например, Hash в Boost :: unordered_map). Так что, возможно, он должен сохранить базовый тип карты. Я думаю, это можно сделать с помощью чего-то вроде следующего:
AnyMap& AnyMap::operator=(const AnyMap& other) {
mapConcept->clear();
mapConcept->insert( other.mapConcept->begin(), other.mapConcept->end() );
return *this;
}
Это должно работать из-за шаблонного метода вставки:
template <class InputIterator>
void insert (InputIterator first, InputIterator last);
Кстати, если кому-то интересно, как я поступил с итераторами: я использовал any_iterator Томаса Беккера — http://thbecker.net/free_software_utilities/type_erasure_for_cpp_iterators/any_iterator.html.
Так что вы думаете, ребята? Я склоняюсь к последнему подходу, но я хотел бы услышать любые аргументы для любой стороны.
Заранее спасибо!
РЕДАКТИРОВАТЬ: Вот первый аргумент против (возможно, вы могли бы сказать мне, насколько это важно): содержимое одного типа карты теперь может отображаться один в один на другую карту, если один предикат различает два ключа на карте, а другой считает их одинаковыми.
Вы хотите, чтобы оболочка карты имела семантику значений или нет? Это будет определять, насколько глубокой должна быть копия. В любом случае, реализация other.mapConcept->clone()
будет полиморфным (это, в конце концов, суть техники стирания типа C ++) и приведет к диспетчеризации вызова в подклассе MapModel, который выглядит следующим образом
virtual MapModel *clone() { return new MapModel(m); } // covariant return type
Таким образом, все сводится к тому, что делает конструктор копирования ActualMapType (так как переменная-член m
будет копия.)
Поскольку техника была изобретена для получения семантики значений, я думаю, что сохранение этой функции согласуется с принципом наименьшего сюрприза. Кроме того, дело в том, чтобы иметь фиксированный интерфейс. Реализация (STL или Boost или что-то еще) не имеет отношения к дизайну, поэтому нет смысла пытаться сохранить реализацию в любом конкретном экземпляре объекта.
Кстати, ваша реализация operator = для «стандартного» случая не является безопасной для исключения. Идея копирования и замены (возможно, с помощью специального метода swap ()) работает лучше
AnyMap( AnyMap const& other )
: mapConcept( other.mapConcept ? other.mapConcept->clone() : 0)
{}
AnyMap& operator= ( AnyMap rhs ) // note temporary: invokes copy constructor
{ std::swap( mapConcept, rhs.mapConcept ) ; return *this; }
Других решений пока нет …