迭代不同类型集合的通用代码

时间:2012-01-16 17:38:00

标签: c++ stl

是否有一个优雅的解决方案来使用公共代码来遍历hash_map / unordered_maplist / 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_typeunordered_map,因此collection_type::value_type pair ch.second->send(head, cnt)显然无法编译,因此访问实际值的代码应该不同:{{1}而不是ch->send(head, cnt)。那么,当不需要关键部分时,最优雅的方法是什么呢?

2 个答案:

答案 0 :(得分:4)

是:

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);
}

如果基于范围的forauto都不可用,您可以编写一个模板,其中包含容器C并使用C::value_typeC::iterator;或者您可以创建一个接受Iter类型的迭代器的模板,并使用std::iterator_traits<Iter>::value_type作为元素值类型。

第三,您可以使用for_each和lambda:

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)来获取值。

答案 1 :(得分:3)

问题是list / vector只包含一个值,而map-s包含一对键值。它们不是同一个东西,并且以相同的方式迭代,至少定义你感兴趣的那一部分。

一旦定义,你基本上需要一个接受迭代器的“derefence”操作,并且 - 如果它有一个value_type作为一对,返回第二个元素,或者只是取消引用它。

// 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));
无论容器如何,

都是一样的。