#c #std #forward-declaration
#c #std #пересылка-объявление
Вопрос:
У меня есть пользовательский шаблонный контейнер, использующий синхронизацию карты и списка. Карта должна содержать myList::const_iterator, а список должен содержать myMap::const_iterator. Единственное решение, которое я смог найти, — это отключить один из итераторов, как в примере ниже.
Есть ли правильный способ переслать объявление об этом, чтобы мне не нужен был уродливый каламбур?
Доступный для выполнения код доступен по адресуhttp://coliru.stacked-crooked.com/a/a5eae03ad5090b27.
(Есть определенно другие подходы, которые можно было бы использовать для примера, но это выходит за рамки. Это фрагмент более крупной программы. Я просто пытаюсь создать это «круговое» определение без UB.)
#include <iostream>
#include <list>
#include <unordered_map>
template<class ObjectT> class MyClass
{
private:
// SUMMARY: The map must contain an iterator to the list, and the list must contain an iterator to the map.
// I have not been able to figure out how to define that (circular), so I've punned an iterator to a different list for the map entry.
typedef std::list<ObjectT> PunnedList;
struct MapEntry
{
ObjectT m_object;
mutable typename PunnedList::const_iterator m_listIt; // Really a List::const_iterator, but that can't be defined.
};
typedef std::unordered_multimap<std::string, MapEntry> Map;
public:
struct ListEntry
{
typename Map::iterator m_mapIt;
const ObjectTamp; object() const
{
return m_mapIt->second.m_object;
}
const std::stringamp; name() const
{
return m_mapIt->first;
}
};
private:
typedef std::list<ListEntry> List;
Map mMap;
List mList;
private:
typename List::const_iterator listiter_from_mapiter( typename Map::const_iteratoramp; miter ) const
{
static_assert(sizeof(typename PunnedList::const_iterator) == sizeof(typename List::const_iterator));
return *(reinterpret_cast<typename List::const_iterator*>(amp;miter->second.m_listIt));
}
public:
typename List::const_iterator append( const std::string amp;name, const ObjectTamp; item )
{
static_assert(sizeof(typename PunnedList::const_iterator) == sizeof(typename List::const_iterator));
MapEntry entry{ item, typename PunnedList::const_iterator{} };
auto mapIter = mMap.insert({ name, entry });
mList.push_back({ mapIter });
auto iter = mList.cend();
--iter;
*(reinterpret_cast<typename List::const_iterator*>(amp;mapIter->second.m_listIt)) = iter;
return iter;
}
typename List::const_iterator begin() const
{
return mList.end();
}
typename List::const_iterator end() const
{
return mList.end();
}
void erase( typename List::const_iterator iter )
{
mMap.erase(iter->m_mapIt);
mList.erase( iter );
}
typename List::const_iterator find( const std::string amp;name ) const
{
auto range = mMap.equal_range(name);
for (auto mapIter = range.first; mapIter != range.second; mapIter)
{
// In the real program, there are additional criteria on the map entry, not needed for the example.
// if (mapIter is a match)
return listiter_from_mapiter(mapIter);
}
return mList.cend();
}
};
int main()
{
MyClass<int> container;
container.append("A",1);
container.append("B",2);
container.append("C",1);
std::cout << container.find("B")->object();
}
Ответ №1:
невозможно объявить тип, T
который зависит от типа Y
, который также зависит от T
. но вы можете использовать оболочку для этого:
(кажется, вы хотите создать хэш-таблицу, в которой элементы находятся в порядке вставки? обычной практикой является перенос элемента и итератора связанного списка.)
#include <string>
#include <list>
#include <unordered_map>
template<typename T>
class A{
struct Wrapper;
typedef std::unordered_multimap<std::string, Wrapper> Map;
typedef typename Map::const_iterator map_iter;
typedef std::list<map_iter> List;
typedef typename List::const_iterator list_iter;
struct Wrapper{
T value;
list_iter iter;
};
Map map;
List list;
public:
// do what you want
};
это работает, потому что для объявления std::unordered_multimap<std::string, Wrapper>::iterator
не обязательно определять Wrapper
(здесь не нужно создавать экземпляр).
Комментарии:
1. Спасибо. Я не понимал, что вы можете объявить это в прямом определении!
Ответ №2:
Пересылка — объявление хотя бы одного из ваших внутренних классов прерывает цикл:
template<class ObjectT> class MyClass
{
public:
struct ListEntry;
private:
typedef std::list<ListEntry> List;
// Rest of the class, using List as you like
Обратите внимание, что следующее List::const_iterator
работает благодаря тому, что для создания экземпляра std::list<T>
не требуется T
завершения.
Комментарии:
1. Спасибо. Я не понимал, что вы можете объявить это в прямом определении! Принимая этот ответ таким, каким он был первым.