Итератор для значений карты C++11 (простой и прозрачный)

Я ищу простой способ создать итератор для значения map В C++11.

этот метод должен быть простым и прозрачным: простым в том, что он должен быть простым в реализации, и прозрачным в том, что клиент не должен знать, что значения приходят с карты, а не набора.

этот вопрос задавался несколько раз раньше. Многие из этих вопросов предшествуют C++11 и используют boost, который я не хочу использовать. Некоторые из них не просты, решение Джона Альгрена вот,http://john-ahlgren.blogspot.com/2013/10/how-to-iterate-over-values-of-stdmap.html, например, требуется страница кода для записи пользовательского итератора.

остальные не прозрачны, т. е. ясно можно написать:

map<string,foo> mymap;
for (auto it=mymap.begin();it!=mymap.end();++it){
  Foo val= it->second;
  ...
 }

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

проблема возникает следующим образом.

у меня есть куча объектов, однозначно индексируется длинным "ключом". Иногда я хочу манипулировать наборами этих объектов. В других случаях я хочу получить объект, учитывая его ключ.

Я не могу использовать класс "set" напрямую по нескольким причинам, главная из которых заключается в том, что он не хранит изменяемые экземпляры, и эти экземпляры должны быть изменяемыми (за исключением, очевидно, ключа).

Итак, я решил сохранить все свои объекты в гигантской глобальной хэш-таблице:

map<long,Foo> all_the_objects;

Я тогда не работаю с set<Foo> at все. Вместо этого я работаю с set<long> и используйте адаптер для имитации набора Foo, т. е.

class SetOfFoo{
  private: set<long> theKeys;
  public:
    void insert(const & Foo);
    size_t size() return theKeys.size();
    bool is_member(const & Foo)
      {return theKeys.find(Foo.key)
          != theKeys.end;}
    Foo & insert(const & Foo val){
       long key=val.key;
       all_the_objects[key]=val;
       return all_the_objects[key];
     }
    ...::iterator begin() {???}
}

другими словами, клиент класса SetOfFoo не знает или не должен знать, что SetOfFoo реализован как набор ключей.

Я также не могу просто сделать вектор сам в классе adaptor, потому что нельзя хранить ссылки в коллекциях C++.

действительно ли невозможно сделать простой, прозрачный способ перебора значений map? Мне трудно поверьте, поскольку это очень распространенная потребность и тривиально делать на каждом языке, который я видел, который имеет хэш-таблицы. Я просто не понимаю, как это может быть тяжело.

2 ответов


это довольно тривиально.

вот чрезвычайно упрощенная версия, которая минимально решает проблему для отображения ints в строки. Вы можете либо переписать для типов вы хотите или templatise его, как вы хотите.

#include <map>
#include <iostream>
#include <iterator>
#include <string>
#include <algorithm>

struct map_value_iterator : public std::map<int, std::string>::const_iterator
{
    map_value_iterator(std::map<int, std::string>::const_iterator src)
    : std::map<int, std::string>::const_iterator(std::move(src))
    {

    }

    // override the indirection operator
    const std::string& operator*() const {
        return std::map<int, std::string>::const_iterator::operator*().second;
    }
};


using namespace std;


int main()
{
    map<int, string> myMap { {1, "Hello" }, { 2, "World" } };

    copy(map_value_iterator(begin(myMap)), map_value_iterator(end(myMap)), ostream_iterator<string>(cout , " "));
    cout << endl;

    return 0;
}

программа:

Compiling the source code....
$g++ -std=c++11 main.cpp -o demo -lm -pthread -lgmpxx -lgmp -lreadline 2>&1

Executing the program....
$demo 
Hello World 

вы можете сделать что-то вроде следующего (C++98):

#include <iostream>
#include <map>
#include <string>
#include <algorithm>

#include "util/pair_iterator.hpp"

template<class T> inline T const& constify(T& t) { return t; }

int main()
{
    using namespace std;
    using namespace util;

    map<int, string> m;
    m[0] = "alice";
    m[1] = "bob";
    m[2] = "carol";
    m[3] = "dave";
    m[4] = "eve";

    copy(
          over_second(m.begin())
        , over_second(m.end())
        , ostream_iterator<string>(cout, "\n")
        );

    copy(
          over_first(m.begin())
        , over_first(m.end())
        , ostream_iterator<int>(cout, "\n")
        );

    // const iterators check

    copy(
          over_second(constify(m).begin())
        , over_second(constify(m).end())
        , ostream_iterator<string>(cout, "\n")
        );

    copy(
          over_first(constify(m).begin())
        , over_first(constify(m).end())
        , ostream_iterator<int>(cout, "\n")
        );
}

вот реализация:

// util/pair_iterator.hpp
#include <iterator>

#include "boost/iterator/transform_iterator.hpp"
#include "boost/type_traits/remove_reference.hpp"
#include "boost/type_traits/is_const.hpp"
#include "boost/mpl/if.hpp"

namespace util {

namespace aux {

template<class T> struct dereference_type
    : boost::remove_reference<typename std::iterator_traits<T>::reference>
{
};

template<class PairT>
struct first_extracter
{
    typedef typename boost::mpl::if_<
          boost::is_const<PairT>
        , typename PairT::first_type const
        , typename PairT::first_type
        >::type result_type;
    result_type& operator()(PairT& p) const { return p.first; }
};

template<class PairT>
struct second_extracter
{
    typedef typename boost::mpl::if_<
          boost::is_const<PairT>
        , typename PairT::second_type const
        , typename PairT::second_type
        >::type result_type;
    result_type& operator()(PairT& p) const { return p.second; }
};

} // namespace aux {

template<class IteratorT>
inline
boost::transform_iterator<aux::first_extracter<typename aux::dereference_type<IteratorT>::type>, IteratorT>
over_first(IteratorT const& i)
{
    typedef aux::first_extracter<typename aux::dereference_type<IteratorT>::type> extracter;
    return boost::transform_iterator<extracter, IteratorT>(i, extracter());
}

template<class IteratorT>
inline
boost::transform_iterator<aux::second_extracter<typename aux::dereference_type<IteratorT>::type>, IteratorT>
over_second(IteratorT const& i)
{
    typedef aux::second_extracter<typename aux::dereference_type<IteratorT>::type> extracter;
    return boost::transform_iterator<extracter, IteratorT>(i, extracter());
}

} // namespace util