【问题标题】:Creating a STL map key-iterator创建 STL 映射键迭代器
【发布时间】:2011-10-05 20:30:40
【问题描述】:

通常,您有一个像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 应该是轻量级的,因此您可以从几乎没有开销的方法中返回它。

编辑: 我不确定我解释得是否完美,让我给出一个更好的用例(半伪):

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

【问题讨论】:

  • 让 KeyIterator 持有指向映射的指针,并有一个迭代器包装类来包装映射的迭代器,将调用转发到 ++、==、-- 等。应该相对简单,虽然有它仅以键类型为模板将需要某种类型擦除魔法
  • stackoverflow.com/questions/681943/… 这可能会有所帮助
  • 嘿@John:说真的,我认为没有任何意义包含在另一个类中。此外,查看@Mark Ransom 和 Mooing Duck 的答案,我没有看到太大的改进,仍然从 @ begin() 开始并在 end() 结束。

标签: c++ stl


【解决方案1】:
#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 类型,而不是 key 类型,这意味着如果您试图隐藏内部结构,则必须提供一些实现细节。

【讨论】:

  • 我刚刚注意到这是在键盘上编译并运行的,但我没有资格std::string。什么?
  • 因为键盘会插入各种东西,包括using namespace std。见codepad.org/sKvDs2Et。不幸的是,我无法弄清楚我之前是如何获得prelude.h 的内容的。我不知道它在哪里,你知道cpp不能被调用。
  • 哦...改用 ideone(还有 C# 和 C++0x 以及许多其他不错的位。当然没有提升 :))
  • @sehe ideone 有提升,但仅限于纯 C++,而不是 C++0x 模式。并且只能使用仅包含头文件的 boost 库。
  • @Cubbi:谢谢通知。我绝对确定我曾多次尝试过 Spirit。下次我会重新检查
【解决方案2】:
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。您将无法仅在键类型上对其进行模板化,因为它必须包含对地图的某种引用;您需要地图的完整定义。

您的请求使问题过于复杂,因为您必须定义两种不同的类型,KeyIteratorKeyIterator::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; 
}

【讨论】:

  • 这就是迭代器本身,但我们不需要一个容器来循环从begin()end() 的内容吗?
  • I wish the map iterator had typedefs for the types it points to... 你的意思是像std::iterator_traits&lt;iterator_type&gt;::value_type
  • @MooingDuck,谢谢!我期待迭代器类型本身的 typedef,完全忘记了 iterator_traits(我从未使用过它)。
  • @John,您只需使用 KeyIterator(mymap.begin())KeyIterator(mymap.end())
  • @MarkRansom - 我更新了我的问题,要么我误解了你的答案,要么问题解释得不好。
【解决方案3】:

我猜主要问题是您只需要一个模板参数而不是两个:KeyIterator&lt;string&gt; 而不是 KeyIterator&lt;string, X&gt;

要实现这一点,您可能需要一种称为类型擦除的技术。不过,这将涉及动态分配和虚拟方法调用。

如果是后者,您可以只包装一个map&lt;string, X&gt;::const_iterator,使解引用返回对键的引用并提供采用映射迭代器的构造函数。

【讨论】:

    猜你喜欢
    • 2011-04-02
    • 2011-05-29
    • 2011-05-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-05-11
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多