Перебирать последовательность STL и ассоциативные контейнеры, используя один и тот же код? - PullRequest
0 голосов
/ 05 февраля 2011

Допустим, я хотел бы написать алгоритм, который печатает значение каждого элемента в контейнере. Контейнер может быть контейнером Sequence или Associative (например, std::vector или std::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 ]

4 голосов
/ 05 февраля 2011

Разница между вашими двумя шаблонами функций не в разнице между ассоциативными контейнерами и последовательностями, а в части хранимого типа.

Чтобы уточнить, std::set является ассоциативным контейнером, но будет работать с вашей printSequence функцией; проблема с map не в том, что она ассоциативна, а в том, что value_type является pair и вас интересует только часть second.

Самое простое, что нужно сделать - это абстрагировать операцию разыменования.

например. используется так:

#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 голос
/ 05 февраля 2011

Я вытащил адаптер итератора, основываясь на ответе Чарльза.Я публикую его здесь на случай, если кто-нибудь найдет это полезным:

#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.

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