Итератор для значений карты 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