使用相同的代码迭代STL序列和关联容器?

时间:2011-02-05 17:21:22

标签: c++ stl

假设我想编写一个算法来打印容器中每个元素的值。容器可以是SequenceAssociative容器(例如std::vectorstd::map)。在序列的情况下,算法将打印value_type。在关联类型的情况下,算法将打印data_type。如何编写我的算法(仅一次!)以便它可以与任何一个一起使用?假设算法很复杂,并且我不想为序列/关联版本重复它。

例如:

template <class Iterator>
void printSequence(Iterator begin, Iterator end)
{
    for (Iterator it=begin; it!=end; ++it)
        std::cout << *it;
}

template <class Iterator>
void printAssociative(Iterator begin, Iterator end)
{
    for (Iterator it=begin; it!=end; ++it)
        std::cout << it->second;
}

template <class Iterator>
void printEither(Iterator begin, Iterator end)
{
    // ????
}

2 个答案:

答案 0 :(得分:4)

两个函数模板之间的区别不在于关联容器和序列之间的差异,而是存储类型部分的差异。

为了澄清,std::set是一个关联容器,但可以与printSequence函数一起使用; map的问题不在于它是关联的,而是value_typepair,您只对second部分感兴趣。

最简单的方法是抽象解除引用操作。

E.g。像这样使用:

#include <map>
#include <vector>

template< class X, class Y >
void test( const std::map<X, Y>& mp )
{
    printEither( mp.begin(), mp.end(), MakeMapDerefence( mp ) );
}

template< class Y >
void test( const std::vector<Y>& vec )
{
    printEither( vec.begin(), vec.end(), MakeSimpleDereference( vec ) );
}

定义如下(有一个锅炉板可能是一个提升单线):

template< class ReferenceType, class IteratorType >
struct SimpleDereference
{
    ReferenceType operator() ( IteratorType i ) const
    {
        return *i;
    }
};

template< class ReferenceType, class IteratorType >
struct MapDereference
{
    ReferenceType operator() ( IteratorType i ) const
    {
        return i->second;
    }
};

// Helper template function to make an appropriate SimpleDerefence instance
template< class Container >
SimpleDereference< typename Container::const_reference
                 , typename Container::const_iterator >
MakeSimpleDereference( const Container& )
{
    return SimpleDereference< typename Container::const_reference
                            , typename Container::const_iterator >();
}

// Helper template function to make an appropriate SimpleDerefence instance
template< class Container >
SimpleDereference< typename Container::reference
                 , typename Container::iterator >
MakeSimpleDereference( Container& )
{
    return SimpleDereference< typename Container::reference
                            , typename Container::iterator >();
}

// Helper template function to make an appropriate MapDerefence instance
template< class Container >
MapDereference< const typename Container::mapped_type&
              , typename Container::const_iterator >
MakeMapDerefence( const Container& )
{
    return MapDereference< const typename Container::mapped_type&
                         , typename Container::const_iterator >();
}

// Helper template function to make an appropriate MapDerefence instance
template< class Container >
MapDereference< typename Container::mapped_type&
              , typename Container::iterator >
MakeMapDereference( Container& )
{
    return MapDereference< typename Container::mapped_type&
                         , typename Container::iterator >();
}

#include <iostream>
#include <ostream>

template <class Iterator, class Dereference> void printEither(Iterator begin, Iterator end, Dereference deref)
{
    for (; begin != end; ++begin)
    {
        std::cout << deref(begin);
    }
}

答案 1 :(得分:1)

我根据Charles的回答掀起了一个迭代器适配器。我在这里张贴以防万一有人发现它有用:

#include <iostream>
#include <map>
#include <vector>
#include <boost/iterator/iterator_adaptor.hpp>

//------------------------------------------------------------------------------
template <class Iterator>
void print(Iterator begin, Iterator end)
{
    for (Iterator it=begin; it!=end; ++it)
        std::cout << *it << "\n";
}


//------------------------------------------------------------------------------
template <class BaseIterator>
class MapDataIterator :
    public boost::iterator_adaptor<
        MapDataIterator<BaseIterator>,
        BaseIterator,
        typename BaseIterator::value_type::second_type >
{
public:
    typedef typename BaseIterator::value_type::second_type& reference;

    MapDataIterator() {}

    explicit MapDataIterator(BaseIterator base)
    :   MapDataIterator::iterator_adaptor_(base) {}

 private:
    friend class boost::iterator_core_access;
    reference dereference() const
        {return this->base_reference()->second;}
};

//------------------------------------------------------------------------------
int main()
{
    std::vector<int> vec;
    vec.push_back(31);
    vec.push_back(41);
    std::map<int,int> map;
    map[31] = 41;
    map[59] = 26;

    typedef MapDataIterator< std::map<int,int>::iterator > DataIter;
    print( vec.begin(), vec.end() );
    print( DataIter(map.begin()), DataIter(map.end()) );
}

此解决方案的另一个优点是算法无需知道如何取消引用迭代器。对于任何需要“数据序列”的现有算法,它也是可重用的。

我很惊讶这个小动物在Boost中不存在。