Нужна карта C и список, которые содержат итераторы друг к другу

#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. Спасибо. Я не понимал, что вы можете объявить это в прямом определении! Принимая этот ответ таким, каким он был первым.