Простой способ перебора std :: map <...>? - PullRequest
3 голосов
/ 10 августа 2011

У меня есть переменная с типом, похожим на:

map<bool, map<string, pair<string, int> > > items;

, который я передаю различным функциям.

Есть ли менее утомительный способ перебрать ее, затем сказать:

for (map<bool, map<string, pair<string, int> > >::iterator p = items.begin();
    p != items.end(); p++)
    ...

каждый раз?(т.е. можно ли как-то опустить имя типа, с макросом или шаблоном или чем-то? Руководство typedef не в счет.)

Я использую Visual C ++ 2008.

Ответы [ 8 ]

6 голосов
/ 10 августа 2011

Вы можете использовать BOOST_FOREACH.Вы должны будете использовать typedef для ясности:

typedef std::map<std::string, std::pair<std::string, int> > inner_map;
typedef std::pair<bool, inner_map> map_entry;

BOOST_FOREACH(map_entry& p, items)
{
    ...
}

Я предпочитаю простой typedef и цикл for.Я вижу typedef точно так же, как вижу назначение переменной:

typedef std::map<std::string, std::pair<std::string, int> > inner_map;
typedef std::map<bool, inner_map>::iterator map_iterator;

for (map_iterator i = items.begin(); i != items.end(); ++i)
{
    ...
}

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

Или вы можете использовать обычный std::for_each, если вы готовы написать функтор.Мне не очень нравится это в стандартном C ++, так как тело цикла больше не является локальным (однако в некоторых случаях это может быть преимуществом):

struct some_functor
{
    template <typename K, typename V>
    void operator()(std::pair<K, V>& item)
    {
        // In the context below, K is bool and
        // V is map<string, pair<string, int> >
    }
};

, а затем

std::for_each(items.begin(), items.end(), some_functor());

Если вы обновитесь до VS2010, у вас есть альтернативы: auto и std::for_each с лямбдой (что я предпочитаю).С C ++ 0x, технически, у вас также есть циклы на основе диапазона (недоступно в VS2010).

В заключение я бы сделал:

class meaningful_data
{
    typedef std::map<std::string, std::pair<std::string, int> > inner_map;
    std::map<bool, inner_map> items;

public:
    typedef std::pair<bool, inner_map> value_type;
    typedef std::map<bool, inner_map>::iterator iterator;
    typedef std::map<bool, inner_map>::const_iterator const_iterator;

    iterator begin() { return items.begin(); }
    const_iterator begin() const { return items.begin(); }
    iterator end() { return items.end(); }
    const_iterator end() const { return items.end(); }

    // Add some interface here (as small as possible)
};

и итерацию следующим образом:

for (meaningful_data::iterator i = d.begin(); i != d.end(); ++i)
{
    ...
}

или

BOOST_FOREACH(meaningful_data::value_type& i, d)
{
    ...
}

Возможно, вы захотите инкапсулировать такой сложный тип, по крайней мере, с несколькими определениями типов (вы не обязаны использовать полноценный классесли тип inner_map должен быть открытым).

4 голосов
/ 10 августа 2011

Я рекомендую использовать typedef, что, вероятно, является способом сказать "нет, ты не можешь";)

В противном случае, если вы переключитесь на компилятор, который поддерживает auto, как определено в C ++ 0x, вы можете сказать:

for (auto p = items.begin(); p != items.end(); ++p) // ...

(О, кстати, я также рекомендую ++p, чтобы не копировать итератор)

3 голосов
/ 10 августа 2011

Вы можете использовать стандартный алгоритм for_each:

#include <algorithm>

struct your_functor {
  template<typename T>
  void operator()(T const &item) {
    // Your loop body here.
  }
}

std::for_each(items.begin(), items.end(), your_functor());
2 голосов
/ 10 августа 2011

Вы можете использовать BOOST_AUTO

1 голос
/ 10 августа 2011

Вы можете написать свой собственный алгоритм функции.

template<class C>
void do_what_I_want_to_do(C& c)
{
    for (C::iterator i = c.begin(); i != c.end(); ++c)
    {
        // do something
    }
}

do_what_I_want_to_do(items);

Это может или не может быть улучшение для вас.

0 голосов
/ 23 декабря 2012

Qt предлагает свою собственную реализацию foreach, так что я просто переработал ее для std :: map - в основном просто простая модификация (-> секунда).Протестировано на MSVC и gcc.

struct ForeachBaseBase {};

template <typename T1, typename T2>
class ForeachBase: public ForeachBaseBase
{
public:
    inline ForeachBase(const std::map<T1,T2>& t): c(t), brk(0), i(c.begin()), e(c.end()){}
    const std::map<T1,T2> c;
    mutable int brk;
    mutable typename std::map<T1,T2>::const_iterator i, e;
    inline bool condition() const { return (!brk++ && i != e);}
};

template <typename T1, typename T2> inline std::map<T1,T2> *pMForeachPointer(const std::map<T1,T2> &) { return 0; }

template <typename T1, typename T2> inline ForeachBase<T1,T2> pMForeachBaseNew(const std::map<T1,T2>& t)
{ return ForeachBase<T1,T2>(t); }

template <typename T1, typename T2>
inline const ForeachBase<T1,T2> *pMForeachBase(const ForeachBaseBase *base, const std::map<T1,T2> *)
{ return static_cast<const ForeachBase<T1,T2> *>(base); }


#if defined(Q_CC_MIPS)
/*
   Proper for-scoping in MIPSpro CC
*/
#  define MAP_FOREACH(variable,container)                                                             \
    if(0){}else                                                                                     \
    for (const ForeachBaseBase &_container_ = pMForeachBaseNew(container);                \
         pMForeachBase(&_container_, true ? 0 : pMForeachPointer(container))->condition();       \
         ++pMForeachBase(&_container_, true ? 0 : pMForeachPointer(container))->i)               \
        for (variable = pMForeachBase(&_container_, true ? 0 : pMForeachPointer(container))->i->second; \
             pMForeachBase(&_container_, true ? 0 : pMForeachPointer(container))->brk;           \
             --pMForeachBase(&_container_, true ? 0 : pMForeachPointer(container))->brk)

#elif defined(Q_CC_DIAB)
// VxWorks DIAB generates unresolvable symbols, if container is a function call
#  define MAP_FOREACH(variable,container)                                                             \
    if(0){}else                                                                                     \
    for (const ForeachBaseBase &_container_ = pMForeachBaseNew(container);                \
         pMForeachBase(&_container_, (__typeof__(container) *) 0)->condition();       \
         ++pMForeachBase(&_container_, (__typeof__(container) *) 0)->i)               \
        for (variable = pMForeachBase(&_container_, (__typeof__(container) *) 0)->i->second; \
             pMForeachBase(&_container_, (__typeof__(container) *) 0)->brk;           \
             --pMForeachBase(&_container_, (__typeof__(container) *) 0)->brk)

#else
#  define MAP_FOREACH(variable, container) \
    for (const ForeachBaseBase &_container_ = pMForeachBaseNew(container); \
         pMForeachBase(&_container_, true ? 0 : pMForeachPointer(container))->condition();       \
         ++pMForeachBase(&_container_, true ? 0 : pMForeachPointer(container))->i)               \
        for (variable = pMForeachBase(&_container_, true ? 0 : pMForeachPointer(container))->i->second; \
             pMForeachBase(&_container_, true ? 0 : pMForeachPointer(container))->brk;           \
             --pMForeachBase(&_container_, true ? 0 : pMForeachPointer(container))->brk)
#endif // MSVC6 || MIPSpro

#define mforeach MAP_FOREACH
0 голосов
/ 10 августа 2011

После просмотра всех ответов «нет, ты не можешь сделать это», я потратил некоторое время, чтобы попытаться найти хотя бы частичный обходной путь.

Эта версия почти работает с предупреждением, что каждая ссылка на итератор также требует ссылки на контейнер .Возможно, не очень хорошая идея использовать это (из-за выделения кучи и других вещей), но я решил поделиться этим в любом случае:

#include <map>
#include <iostream>
using namespace std;

template<typename T>
bool _end(T& src, void *iterator = NULL)
{ return static_cast<typename T::iterator>(iterator) < src.end(); }

template<typename T>
struct _IterateHelper
{
    typename T::iterator *pIterator;
    _IterateHelper(T& dummy, void *&p)
    { this->pIterator = static_cast<typename T::iterator *>(p); }
    ~_IterateHelper() { delete pIterator; }
};

template<typename T>
_IterateHelper<T> _iterateHelper(T& dummy, void *&p)
{ return _IterateHelper<T>(dummy, p); }

template<typename T>
bool _iterate(T& container, void *&iterator)
{
    typename T::iterator *&p =
        reinterpret_cast<typename T::iterator *&>(iterator);
    if (iterator == NULL) { p = new typename T::iterator(container.begin()); }
    else { ++*p; }
    return *p != container.end();
}

template<typename T>
typename T::iterator & I(T& container, void *&pIterator)
{ return *static_cast<typename T::iterator *>(pIterator); }

#define FOR_EACH(state, container) \
    void *state = NULL; \
    for (_iterateHelper(container, state); _iterate(container, state); )


int main()
{
    map<string, string> m;
    items["a"] = "b";
    items["1"] = "2";
    FOR_EACH(p, items)
        cout << I(items, p)->first << ": " << I(items, p)->second << endl;
}
0 голосов
/ 10 августа 2011

Конечно, используйте typedefs:

typedef std::map<std::string, std::pair<std::string, int> > Item;
typedef Item::const_iterator                                ItemCItr;

typedef std::map<bool, Item>                                ItemMap;
typedef ItemMap::const_iterator                             ItemMapItr;

for (ItemMapItr it = m.begin(), end = m.end(); it != end; ++it)
{
  const Item & item = it->second;

  for (ItemItr jt = item.begin(), jend = item.end(); jt != jend; ++jt)
  {
     /* ... */
  }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...