Возвращаемое значение ухудшается с помощью шаблонов std :: map и boost :: ptr_map и наследования

В компании, в которой я работаю, мы создали класс RestrictedMap. Это обеспечивает тот же интерфейс, что и обычная std :: map, но не позволяет использовать оператор []. Некоторые другие функции были предоставлены для комфортной работы с классом. Внутренне класс оборачивает std :: map.

Сейчас я пытаюсь создать аналогичный класс, который делает то же самое для boost :: ptr_map, под названием «RestrictedPointerMap». Для этого я создал RestrictedMapBase который принимает в качестве аргумента шаблона тип карты, которую следует обернуть, и содержит большую часть реализации. Два класса наследуют его и определяют тип карты, которую нужно обернуть:

  • RestrictedMap он делает то же самое, что и раньше, и оборачивает std :: map
  • а также RestrictedPointerMap это ново и обертывает boost :: ptr_map.

Вот код, я не упростил класс для полноты, но позже я назову соответствующие функции.

RestrictedMap.h

    #pragma once

#include <boost/ptr_container/ptr_map.hpp>
#include <boost/static_assert.hpp>
#include <boost/assign/list_of.hpp>
#include <boost/foreach.hpp>
#include <map>

/**
* Class that has the benefits of a map, but does not add an entry if it does not exists.
* Do not use RestrictedMapBase directly but use one of the derived classes (RestrictedMap or RestrictedPointerMap).
*/
template <typename MAP>
class RestrictedMapBase
{
public:
RestrictedMapBase(const MAP& map):
m_map(map)
{}

template<class InputIterator>
RestrictedMapBase(InputIterator first, InputIterator last):
m_map(first, last)
{}

RestrictedMapBase()
{}

/************************************************************************/
/* std::map interface                                                   */
/************************************************************************/

typedef typename MAP::iterator iterator;
typedef typename MAP::const_iterator const_iterator;
typedef typename MAP::value_type value_type;
typedef typename MAP::key_type key_type;
typedef typename MAP::mapped_type mapped_type;
typedef typename MAP::size_type size_type;

iterator begin() { return m_map.begin(); }
iterator end() { return m_map.end(); }
const_iterator begin() const { return m_map.begin(); }
const_iterator end() const { return m_map.end(); }
bool empty() const { return m_map.empty(); }
size_type size() const { return m_map.size(); }
iterator find(const key_type& key) { return m_map.find(key); }
const_iterator find(const key_type& key) const { return m_map.find(key); }
void clear() { m_map.clear(); }
void erase(iterator where) { m_map.erase(where); }

bool operator==(const typename RestrictedMapBase<MAP>& other) const { return m_map == other.m_map; }
bool operator!=(const typename RestrictedMapBase<MAP>& other) const { return m_map != other.m_map; }
bool operator<(const typename RestrictedMapBase<MAP>& other) const { return m_map < other.m_map; }

/************************************************************************/
/* extra                                                                */
/************************************************************************/

void erase(const key_type& key)
{
iterator iter(find(key));
assert(found(iter));
erase(iter);
}

void eraseIfExists(const key_type& key)
{
m_map.erase(key);
}

bool exists(const key_type& key) const
{
return found(find(key));
}

mapped_type& getValue(const key_type& key)
{
return const_cast<mapped_type&>(static_cast<const RestrictedMapBase<MAP>&> (*this).getValue(key));
}

const mapped_type& getValue(const key_type& key) const
{
const_iterator iter(find(key));
assert(found(iter));
return getData(iter);
}

mapped_type getValueIfExists(const key_type& key) const
{
BOOST_STATIC_ASSERT(boost::is_pointer<mapped_type>::value);
const_iterator iter(find(key));
if (found(iter)) {
return getData(iter);
} else {
return 0;
}
}

void setValue(const key_type& key, const mapped_type& value)
{
iterator iter(find(key));
assert(found(iter));
setData(iter, value);
}

void add(const key_type& key, const mapped_type& value)
{
assert(!exists(key));
insert(key, value);
}

void add(const RestrictedMapBase<MAP>& mapToAdd)
{
BOOST_FOREACH(value_type element, mapToAdd.m_map)
{
add(element.first, element.second);
}
}

void addOrReplace(const key_type& key, const mapped_type& value)
{
iterator iter(find(key));
if (found(iter)) {
setData(iter, value);
} else {
insert(key, value);
}
}

mapped_type* addDefaultConstructed(const key_type& key)
{
assert(!exists(key));
return &m_map[key];
}

private:
bool found(const const_iterator& iter) const
{
return iter != end();
}

const mapped_type& getData(const const_iterator& iter) const
{
return const_cast<const mapped_type&>(iter->second);
}

mapped_type& getData(const iterator& iter)
{
return const_cast<mapped_type&>(static_cast<const RestrictedMapBase<MAP>&>(*this).getData(iter));
}

void setData(const iterator& iter, const mapped_type& value)
{
getData(iter) = value;
}

virtual void insert(const key_type& key, const mapped_type& value) = 0;

protected:
MAP& getMap()
{
return m_map;
}

private:
MAP m_map;
};

template <typename KEYTYPE, typename DATATYPE>
class RestrictedMap: public RestrictedMapBase<std::map<KEYTYPE, DATATYPE> >
{
public:
RestrictedMap(const std::map<typename KEYTYPE, typename DATATYPE>& map): RestrictedMapBase(map)
{}

template<class InputIterator>
RestrictedMap(InputIterator first, InputIterator last): RestrictedMapBase(first, last)
{}

RestrictedMap()
{}

virtual void insert(const KEYTYPE& key, const DATATYPE& value)
{
getMap().insert(std::make_pair(key, value));
}
};

template <typename KEYTYPE, typename DATATYPE>
class RestrictedPointerMap: public RestrictedMapBase<boost::ptr_map<KEYTYPE, DATATYPE> >
{
public:
RestrictedPointerMap(const boost::ptr_map<typename KEYTYPE, typename DATATYPE>& map): RestrictedMapBase(map)
{}

template<class InputIterator>
RestrictedPointerMap(InputIterator first, InputIterator last): RestrictedMapBase(first, last)
{}

RestrictedPointerMap()
{}

virtual void insert(const KEYTYPE& key, DATATYPE* const& value)
{
/* boost::ptr_map::mapped_type does not equal the DATATYPE template parameter passed to it. Therefore this
* functions signature *looks* different from the RestrictedMapBase::insert signature */
getMap().insert(key, std::auto_ptr<DATATYPE>(value));
}
};

Это работает в основном, кроме случаев, когда я хочу позвонить ПолучитьЗначение на RestrictedPointerMap. Функция получить данные возвращает правильное значение, но после этого происходит сбой в ПолучитьЗначение функция. Возвращает неверный указатель (так как в указателе неверный).

Вот некоторый код, который воспроизводит проблему:

TestClass.h

    #pragma once

class SomeClass
{
public:
SomeClass();
virtual ~SomeClass();
};

TestClass.cpp

    #include "stdafx.h"#include "TestClass.h"#include <iostream>

SomeClass::SomeClass()
{
std::cout << "TestClass[" << this << "] created." << std::endl;
}

SomeClass::~SomeClass()
{
std::cout << "TestClass[" << this << "] deleted." << std::endl;
}

TestRestrictedPtrMap.cpp (main)

    #include "stdafx.h"
#include "RestrictedMap.h"#include "TestClass.h"#include <boost/foreach.hpp>

int _tmain(int argc, _TCHAR* argv[])
{
typedef RestrictedPointerMap<int, SomeClass> MapType;
MapType theMap;
theMap.add(1, new SomeClass());
theMap.add(2, new SomeClass());

BOOST_FOREACH(MapType::value_type mapEntry, theMap) {
std::cout << mapEntry.first << " = " << mapEntry.second << std::endl;
}

SomeClass* oneClass = theMap.getValue(1);
std::cout << oneClass << std::endl;
SomeClass* twoClass = theMap.getValue(2);
std::cout << twoClass << std::endl;

std::cin.get();
return 0;
}

Выход этого:

    TestClass[0078A318] created.
TestClass[0078A448] created.
1 = 0078A318
2 = 0078A448
0018FBD4
0018FBD4
TestClass[0078A318] deleted.
TestClass[0078A448] deleted.

Я понятия не имею, почему это идет не так. Насколько я знаю, возвращаемое значение ухудшается от магии.

Заранее благодарю за любую помощь,

Том

1

Решение

У вас есть свисающая ссылка.

Когда вы разыменовываете boost::ptr_map<Key, T>::iterator он строит на лету boost::ptr_container_detail::ref_pair<Key, T *> инициализируется из фактический основной итератор ( std::map<Key, void *>::iterator). Это означает, что T *& (или же const T *&) вернулся из getData ссылается на члена местного временного second член iter->second):

   const mapped_type& getData(const const_iterator& iter) const
{
return const_cast<const mapped_type&>(iter->second); // reference to a temporary
}
^^^^^^ *iter is a temporary value

Это отличается от нормального std::map, где *iter дает ссылку на подобъект значения узла в двоичном дереве карты.

Там нет простого решения без значительного изменения вашего интерфейса, так как нет фактического T * объект в любом месте в памяти, чтобы взять ссылку. Вы могли бы сделать лучше, чтобы изменить подпись вашего RestrictedPointerMap вернуть T сопоставленные значения по указателю или даже по прямой ссылке:

T *getValue(const key_type& key);               // not T *&
const T *getValue(const key_type& key) const;   // not const T *const &
// or
T &getValue(const key_type& key);               // not T *&
const T &getValue(const key_type& key) const;   // not const T *const &
2

Другие решения

Других решений пока нет …