Общий код для перебора различных типов коллекций - PullRequest
1 голос
/ 16 января 2012

Существует ли элегантное решение для использования общего кода для перебора коллекций hash_map / unordered_map и list / vector?

Пример:

template<typename collection>
class multicast
{
public:
    typedef collection collection_type;

private:
    collection_type& m_channels;

public:
    multicast(collection_type& channels) : m_channels(channels) { }

    void operator ()(const buffer::ptr& head, const buffer::ptr& cnt)
    {
        for each(collection_type::value_type& ch in m_channels)
            ch->send(head, cnt); /* this is where the magic should happen? */
    }
}

Этот код, очевидно, не компилируется, когда collection_type равен unordered_map, поскольку collection_type::value_type является pair, поэтому код для доступа к фактическому значению должен быть другим: ch.second->send(head, cnt) вместо ch->send(head, cnt). Так какой же самый элегантный способ избавиться от ключевой части, когда она не нужна?

Ответы [ 2 ]

4 голосов
/ 16 января 2012

Да

for (auto & x : collection) { do_stuff_with(x); }

В качестве альтернативы:

for (auto it = std::begin(collection), end = std::end(collection); it != end; ++it)
{
    do_stuff_with(*it);
}

Если не доступны ни for, ни auto на основе диапазона, вы можете написать шаблон, который принимает контейнер C и использовать C::value_type и C::iterator; или вы можете создать шаблон, который принимает пару итераторов типа Iter и использует std::iterator_traits<Iter>::value_type для типа значения элемента.

В-третьих, вы можете использовать for_each и лямбду:

std::for_each(colllection.begin(), collection.end(),
              [](collection::value_type & x) { do_stuff_with(x); });


Для размещения как одноэлементных, так и парных элементов, вы можете создать небольшую оболочку:

template <typename T> struct get_value_impl
{
    typedef T value_type;
    static value_type & get(T & t) { return t; }
};
template <typename K, typename V> struct get_value_impl<std::pair<K, V>>
{
    typedef V value_type;
    static value_type & get(std::pair<K,V> & p) { return p.second; }
};
template <typename T>
typename get_value_impl<T>::value_type & get_value(T & t)
{
    return get_value_impl<T>::get(t);
}

Теперь вы можете использовать get_value(x) или get_value(*it), чтобы получить только значение.

3 голосов
/ 16 января 2012

Проблема в том, что list / vector содержит только значение, а map-s содержит пару ключ-значение.Это не одно и то же, и, чтобы повторить то же самое, что вы должны, по крайней мере, определить, какая часть пары вас интересует.

После того, как вы определились, вам, по сути, нужна операция «разыменования», которая принимает итератор,и -в случае если в качестве значения_типа пара возвращает второй элемент, в противном случае просто разыменовываете его.

// default case, returning itself
template<class T>
T& get_val(T& t) { return t; } 

// case for pair (derefence for a map iterator)
template<class K, class V>
V& get_val(std::pair<const K, V>& s) { return s.second; }

// iterator dereference
template<class Iter>
decltype(get_val(*Iter()) deref_iter(const Iter& i)
{ return get_val(*i); }

Конечно, const_iter версия также требуется, если необходимо.

Теперь:

for(auto i=container.begin(); i!=container-end(); ++i)
    do_something_with(deref_iter(i));

будет одинаковым независимо от контейнера.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...