Создание ключа-итератора карты STL - PullRequest
7 голосов
/ 06 октября 2011

Часто у вас есть карта типа map<string,X>, где ключом является имя сопоставленного значения, и вам нужен API, который позволяет потребителям видеть все имена ... для заполнения списка GUI, например.Вы можете построить вектор и вернуть его как вызов API, но это довольно неэффективно.Вы можете просто вернуть ссылку на карту, но тогда значения также будут доступны, и вы, возможно, этого не захотите.

Итак, как вы могли бы написать совместимый класс KeyIterator, который переносит карту и предоставляет стандартный доступ итератора кключи на этой карте.

например:

map<string,X> m= ...
KeyIterator<string> ki(m);
for(KeyIterator<string>::iterator it=ki.begin();it!=ki.end();++it)
 cout << *it;

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

edit: Я не уверен, что объяснил отлично, позвольте мне привести лучший вариант использования (полупсевдо):

class PersonManager
{
 private:
  map<string,Person> people;
 public:
  //this version has to iterate the map, build a new structure and return a copy
  vector<string> getNamesStandard();

  //this version returns a lightweight container which can be iterated
  //and directly wraps the map, allowing access to the keys
  KeyIterator<string> getNames();
};

void PrintNames(PersonManager &pm)
{
 KeyIterator<string> names = pm.getNames();
 for(KeyIterator<string>::iterator it=names.begin();it!=names.end();++it)
  cout << *it << endl;
}

Ответы [ 3 ]

3 голосов
/ 06 октября 2011
#include <map>
#include <string>
#include <iterator>

template <class map>
class KeyIterator { 
    typename map::const_iterator iter_;
public:
    KeyIterator() {}
    KeyIterator(typename map::iterator iter) :iter_(iter) {}
    KeyIterator(typename map::const_iterator iter) :iter_(iter) {}
    KeyIterator(const KeyIterator& b) :iter_(b.iter_) {}
    KeyIterator& operator=(const KeyIterator& b) {iter_ = b.iter_; return *this;}
    KeyIterator& operator++() {++iter_; return *this;}
    KeyIterator operator++(int) {return KeyIterator(iter_++);}
    const typename map::key_type& operator*() {return iter_->first;}
    bool operator==(const KeyIterator& b) {return iter_==b.iter_;}
    bool operator!=(const KeyIterator& b) {return iter_!=b.iter_;}
};

int main() {
    std::map<std::string,int> m;
    KeyIterator<std::map<std::string,int> > ki;
    for(ki=m.begin(); ki!=m.end(); ++ki)
        cout << *ki;
}

http://codepad.org/4wxFGGNV
Не намного легче, чем это.Однако для этого требуется, чтобы итератор был настроен на тип map , а не на тип ключа, что означает, что вы должны предоставить некоторые подробности реализации, если вы пытались скрыть внутренние компоненты.

3 голосов
/ 06 октября 2011
template<typename iterator_type>
class KeyIterator
{
    iterator_type iterator;
public:
    typedef typename std::iterator_traits<iterator_type>::value_type::first_type value_type;
    KeyIterator(iterator_type i) : iterator(i) {}
    value_type operator*() { return iterator->first; }
    KeyIterator & operator++() { ++iterator; return *this; }
    bool operator!=(const KeyIterator & right) const { return iterator != right.iterator; }
    // ...
};

Редактировать: После просмотра вашего редактирования я понимаю, что это не совсем то, что вы просили. Вы смутили меня, назвав свой класс KeyIterator, более подходящее имя было бы KeyContainer. Вы не сможете шаблонировать его только по типу ключа, так как он должен содержать какую-то ссылку на карту; Вам понадобится полное определение карты.

Ваш запрос усложняет проблему, потому что вы должны определить два разных типа, KeyIterator и KeyIterator::iterator.

Вот ваш пример кода с использованием моего класса:

class PersonManager
{
private:
    map<string,Person> people;
public:
    //this version has to iterate the map, build a new structure and return a copy 
    vector<string> getNamesStandard(); 

    //this version returns a lightweight container which can be iterated 
    //and directly wraps the map, allowing access to the keys 
    KeyIterator<map<string,Person>::iterator> getNamesBegin(); 
    KeyIterator<map<string,Person>::iterator> getNamesEnd(); 
}; 

void PrintNames(PersonManager &pm) 
{ 
    KeyIterator<map<string,Person>::iterator> it = pm.getNamesBegin();
    KeyIterator<map<string,Person>::iterator> end = pm.getNamesEnd();
    for(it; it!=end; ++it) 
        cout << *it << endl; 
}
0 голосов
/ 06 октября 2011

Думаю, основная проблема в том, что вам нужен только один аргумент шаблона вместо двух: KeyIterator<string> вместо KeyIterator<string, X>.

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

Если это последнее, вы можете просто обернуть map<string, X>::const_iterator, заставить разыменование возвращать ссылку на ключ и предоставить конструктор (ы), который принимает картуитераторы.

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