Как адаптировать набор итераторов, чтобы он вел себя как итератор карты? - PullRequest
5 голосов
/ 12 ноября 2010

У меня есть класс 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 ]

2 голосов
/ 12 ноября 2010

Было бы недостаточно использовать

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

для этой сопоставимости?

Если вы не всегда можете написать свой собственный итератор, который обертывает итератор std :: list и предоставляет first иsecond участников.По сути, ваш оператор ++ будет вызывать оператор ++ на реальном итераторе и т. Д., А оператор разыменования может вернуть либо временную 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 голос
/ 12 ноября 2010

Как насчет этого?

#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;
}
0 голосов
/ 12 ноября 2010

Вы не можете, не полностью.Проблема в том, что вы меняете интерфейс, который всегда будет ломать ваших клиентов.Я бы порекомендовал вам создать две новые функции newBegin и newEnd (или аналогичные), которые имеют ваше новое поведение.В вашем старом интерфейсе вы сохраняете то же самое, но помечаете его как устаревшее.Реализация этого старого интерфейса может использовать один из обходных путей, описанных другими.

0 голосов
/ 12 ноября 2010

Возможно, вы можете определить класс fake_pair, который реализует first и second и поместить set<fake_pair> в Foo.

0 голосов
/ 12 ноября 2010

Возможно, что-то вроде

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

, может быть, в какой-то обертке?

...