Возможно: установить операции на отдельных картах с одинаковым типом ключа? - PullRequest
2 голосов
/ 24 марта 2010

Допустим, у меня есть две карты:

typedef int Id;
std::map<Id, std::string> idToStringMap;
std::map<Id, double>      idToDoubleMap;

И скажем, я хотел бы выполнить операцию набора на клавишах двух карт. Есть ли более простой способ сделать это, чем создать собственный итератор для вставки? , чтобы я мог сделать что-то вроде:

std::set<Id> resultSet;
set_difference( idToStringMap.begin(), idToStringMap.end(), 
                idToDoubleMap.begin(), idToDoubleMap.end(), resultSet.begin() );

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

Ответы [ 2 ]

1 голос
/ 24 марта 2010

Мое решение по совету Иайна:

template <typename T>
class Select1st 
 : public std::unary_function<T&,typename T::first_type>
{
     public:
         int operator() (T & value) const 
         {
             return value.first;
         }
};

template <typename T>
class KeyGrabItorAdapter 
 : public boost::transform_iterator< Select1st<typename T::value_type>,
                                                    typename T::iterator >
{
    public:
        KeyGrabItorAdapter( typename T::iterator itor )
            : boost::transform_iterator<Select1st<typename T::value_type>,
                                                       typename T::iterator>
                  ( itor, Select1st<typename T::value_type>() )         
        {
        }

};

, имеющее вышеизложенное, позволяет следующее:

typedef std::map<int, int>    IntToIntMap;
IntToIntMap    intToIntMapA;
IntToIntMap    intToIntMapB;

typedef std::map<int, double> IntToDoubleMap;
IntToDoubleMap intToDoubleMapA;
IntToDoubleMap intToDoubleMapB;

KeyGrabItorAdapter<IntToIntMap>    grabFirstABegin(    intToIntMapA.begin() ) ;
KeyGrabItorAdapter<IntToIntMap>    grabFirstAEnd(      intToIntMapA.end()   ) ;
KeyGrabItorAdapter<IntToDoubleMap> grabFirstBBegin( intToDoubleMapB.begin() ) ;
KeyGrabItorAdapter<IntToDoubleMap> grabFirstBEnd(   intToDoubleMapB.end()   ) ;

std::set<int> intResultSet;

set_difference( grabFirstABegin, grabFirstAEnd,
                grabFirstBBegin, grabFirstBEnd,
                    inserter( intResultSet, intResultSet.begin()),
                        intToIntMapA.key_comp() );
1 голос
/ 24 марта 2010

Я не думаю, что это возможно при использовании только stl без пользовательского итератора. Вы должны создать общий select_1st_iterator. Это обернет любой итератор в пару и вернет itr-> first при разыменовании.

Примечание: некоторые расширения stl имеют функтор select1st, который принимает пару и возвращает первый элемент. Но я не видел версию итератора.

Если вы планируете написать итератор, я бы предложил вам использовать библиотеку бустера итераторов . Наиболее вероятным кандидатом на select_1st_iterator является transfor_iterator

Предполагая, что select_1st_iterator - это функция, которая создает реальный тип select_1st_iterator_t, она может выглядеть следующим образом:

ПРИМЕЧАНИЕ: ваш код вылетит, если вы не используете insert_iterator для resultSet

template<class T>
select_1st_iterator_t<T> select_1st_iterator<T>(itr)
{
    return select_1st_iterator_t<T>(itr);
}

std::set<Id> resultSet;
set_difference(
    select_1st_iterator(idToStringMap.begin()), 
    select_1st_iterator(idToStringMap.end()),
    select_1st_iterator(idToDoubleMap.begin()),
    select_1st_iterator(idToDoubleMap.end()),
    std::inserter(resultSet, resultSet.begin()) );
...