如何使set迭代器适应地图迭代器的行为?

时间:2010-11-12 14:10:42

标签: c++ stl iterator encapsulation

我有一个包含Foo的课程map,并提供begin()end()个函数来迭代它:

class Foo {
  typedef std::map<int, double> Container;
  typedef Container::const_iterator const_iterator;
  Container c_;
 public:
  const_iterator begin() const { return c_.begin(); }
  const_iterator end() const { return c_.end(); }
  void insert(int i, double d) { c_[i] = d; }
  // ...

};

现在我想在内部从std::map<int, double>更改为std::set<int>,但我不想破坏任何客户端代码。

因此double d函数中的insert现在只会被忽略。并且以下代码仍然有效,其中it->second现在只是0.0

Foo foo;
for(Foo::const_iterator it = foo.begin(); it != foo.end(); ++it) {
  std::cout << it->first << " " << it->second << std::endl;
}

如何在Foo课程中进行这些更改?

换句话说,我如何提供Foo::const_iterator以使新内部std::set<int>::const_iterator的行为与旧版std::map<int,double>::const_iterator相符?

更新:我想摆脱map的原因是内存效率。我有数百万Foo个实例,无法将double值存储在其中。

5 个答案:

答案 0 :(得分:2)

将使用

std::set<std::pair<int, double> >

这种可比性还不够?

如果失败,你总是可以编写自己的迭代器来封装std :: list迭代器并提供firstsecond成员。基本上你的operator ++会在真正的迭代器等上调用operator ++。并且de-referencing运算符可以返回一个临时的std :: pair(按值)或者对一个生成在迭代器本身内的std :: pair的引用(如果你的遗产)代码可以处理那个)。

根据您的情况,更新,稍作设想的示例可能有效:

#include <iostream>
#include <set>

class Foo {
  typedef std::set<int> Container;
  typedef Container::const_iterator legacy_iterator;
  Container c_;

  // legacy iterator doesn't have a virtual destructor (probably?), shouldn't
  // be a problem for sane usage though
  class compat_iterator : public legacy_iterator {
  public:
     compat_iterator(const legacy_iterator& it) : legacy_iterator(it) {
     }

     const std::pair<int,double> *operator->() const {
        static std::pair<int,double> value;
        value = std::make_pair(**this, 0.0);
        // Not meeting the usual semantics!
        return &value;
     }
  };
 public:
  typedef compat_iterator const_iterator;

  const_iterator begin() const { return c_.begin(); }
  const_iterator end() const { return c_.end(); }

};



int main() {

  Foo foo;
  for(Foo::const_iterator it = foo.begin(); it != foo.end(); ++it) {
     std::cout << it->first << " " << it->second << std::endl;
  }

}

答案 1 :(得分:1)

这样的事情怎么样?

#include <iostream>
#include <map>
#include <set>

struct Funky
{
    int first;
    static const double second;

    Funky(int i)
    :   first(i)
    {}
};

const double Funky::second = 0.0;

bool operator<(const Funky& lhs, const Funky& rhs)
{
    return lhs.first < rhs.first;
}

class Foo
{
private:
    //std::map<int,double> m_data;
    std::set<Funky> m_data;
public:
    //typedef std::map<int,double>::const_iterator const_iterator;
    typedef std::set<Funky>::const_iterator const_iterator;

    const_iterator begin() const
    {
        return m_data.begin();
    }

    const_iterator end() const
    {
        return m_data.end();
    }

    void insert(int i, double d)
    {
        //m_data.insert(std::make_pair(i, d));
        m_data.insert(i);
    }
};

int main()
{
    Foo foo;
    foo.insert(23, 9.0);
    for(Foo::const_iterator it=foo.begin(), iend=foo.end(); it!=iend; ++it)
    {
        std::cout << it->first << ' ' << it->second << '\n';
    }
    return 0;
}

答案 2 :(得分:0)

或许类似于

operator int()(const std::pair<int, double>& p) const {
    return p.first;
}

也许在一些包装内?

答案 3 :(得分:0)

也许您可以定义一个实现fake_pairfirst的{​​{1}}类,并将second放在set<fake_pair>内。

答案 4 :(得分:0)

你不能,不完全。问题是你正在改变你的界面,这将永远打破你的客户。我建议你创建newBegin和newEnd(或类似)的两个新函数,它们具有你的新行为。您的旧界面保持不变,但将其标记为折旧。这个旧接口的实现可以使用其他人描述的工作之一。