简体   繁体   English

创建一个STL映射密钥迭代器

[英]Creating a STL map key-iterator

Often, you have a map like map<string,X> where the key is the name of the mapped value, and you need an API which lets consumers see all the names... to populate a GUI list-box for example. 通常,您有一个类似map<string,X>的映射,其中的键是映射值的名称,并且您需要一个API,该API可以使消费者看到所有名称...例如,以填充GUI列表框。 You can build a vector and return it as an API call but this is rather inefficient. 您可以构建一个向量并将其作为API调用返回,但这效率很低。 You could just return a reference to the map, but then the values are also accessible and you might not want that. 您可以只返回对地图的引用,但随后也可以访问这些值,而您可能不希望这样。

So how could you write a compliant class, KeyIterator, which wraps map and provides standard iterator access to the keys in that map. 因此,如何编写一个兼容的类KeyIterator,它包装了地图并提供了对该地图中键的标准迭代器访问。

eg: 例如:

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

KeyIterator should be lightweight so you can return it from a method with virtually no overhead. KeyIterator应该是轻量级的,因此您可以从几乎没有开销的方法中返回它。

edit: I'm not sure I explained perfectly, let me give a better use-case (semi-pseudo): 编辑:我不确定我的解释是否完美,让我给出一个更好的用例(半伪):

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;
}
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; }
    // ...
};

Edit: After seeing your edit I realize this isn't exactly what you asked for. 编辑:看到您的编辑后,我意识到这并非您所要的。 You confused me by calling your class a KeyIterator, a more appropriate name would be KeyContainer. 您通过将您的类称为KeyIterator混淆了我,更合适的名称是KeyContainer。 You won't be able to template it just on the key type, since it's going to have to contain some kind of reference to the map; 您将无法仅在键类型上对其进行模板化,因为它必须包含某种对地图的引用; you'll need the full definition of the map. 您将需要地图的完整定义。

Your request overcomplicates the problem because you must define two different types, KeyIterator and KeyIterator::iterator . 您的请求使问题KeyIterator KeyIterator::iterator复杂,因为您必须定义两种不同的类型, KeyIteratorKeyIterator::iterator

Here's your sample code using my class: 这是使用我的课程的示例代码:

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; 
}
#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 http://codepad.org/4wxFGGNV
Doesn't get much more lightweight than that. 没有比这更轻量的了。 However, it requires the iterator to be templated on the map type, instead of the key type, which means you have to give out some implementation details if you were attempting to hide internals. 但是,它要求迭代器以映射类型而不是键类型为模板,这意味着如果您要隐藏内部结构,则必须给出一些实现细节。

I guess the main problem is that you want only one template argument instead of two: KeyIterator<string> instead of KeyIterator<string, X> . 我想主要的问题是您只需要一个模板参数,而不是两个: KeyIterator<string>而不是KeyIterator<string, X>

To achieve that, you might need a technique called type erasure. 为此,您可能需要一种称为类型擦除的技术。 This will involve dynamic allocation and virtual method calls, though. 不过,这将涉及动态分配和虚拟方法调用。

If it is the latter, you can just wrap a map<string, X>::const_iterator , make dereferencing return a reference to the key and provide constructor(s) that take map's iterators. 如果是后者,则可以只包装map<string, X>::const_iterator ,使解引用返回对键的引用,并提供采用映射迭代器的构造函数。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM