Как «адаптировать» функторы для использования с картой / мультикартой? - PullRequest
1 голос
/ 27 января 2009

Мне трудно использовать std :: for_each и другие алгоритмы с мультикартой, и я хочу знать, может ли кто-нибудь помочь мне в разработке функтора, который мог бы передать соответствующий параметр "универсальным" функциям.

Моя конкретная проблема с map / multimap состоит в том, что их итераторы оценивают как std :: pair вместо содержащегося значения (я имею в виду mapped_type), с которым мне нужно работать. Итак, мой вопрос: есть ли способ передать правильное значение в функцию, предназначенную для работы с одним из содержащихся типов карт / мультикарт?

Вот мой пример кода:

// the key type for the multimap
typedef enum { ... } Module;

// my class as the mapped type for the multimap
struct Patch
{
    void Apply(bool enable);
}

// I have some functors designed to work directly with Patch pointers
// because in other places I use set<Patch*> or other containers
struct ApplyOnCondtion: public unary_function<Patch*, void>
{
    void operator() (Patch* patch)
    {
        if(some_condition) patch->Apply(enable_or_not);
    }
}

// somewhere I have a collection of patches attributed to some module
multimap<Module, Patch*> patches;

// the problem is that using for_each with a map or a multimap results in
// a `pair<Module,Patch*>` as argument to the functor, not the Patch* as desired.
for_each(patches.begin(), patches.end(), ApplyOnCondition(...));

Я думаю, что, возможно, bind1st или bind2nd в сочетании с mem_fun могли бы решить эту проблему, или другой способ, который я могу придумать, - это создать новый функтор, который хранит оригинальный функтор и передает правильный член пары, но Мне не удается получить хороший результат. Может кто-нибудь с опытом работы в STL дать несколько советов?

EDIT 1

Хорошо, лучшее, что я могу получить, без использования boost или дополнительного временного контейнера, это:

#include <functional>
#include <utility>
using namespace std;


//////////////////////////////////////////////////////////////////////////
// any functor to be called must be derived from unary_function or
// have defined result_type and argument_type.
// template 'First' should be set to pair::first_type
template<typename First, typename Func>
class passSecond_t: public unary_function<
                        pair<First,typename Func::argument_type>,
                        typename Func::result_type>
{
    Func* func;

public:
    passSecond_t(Func &functor): func(&functor) {}

    result_type operator()(argument_type value)
    {
        return (*func)(value.second);
    }
};

// construction helper, unfortunately 'First' must be explicitly specified
template <typename First, typename Func>
passSecond_t<First, Func> passSecond(Func& functor)
{
    return passSecond_t<First, Func> (functor);
}


// the following is a sample
#include <map>
#include <algorithm>
#include <iostream>

struct SampleClass 
{
    void execute(char* text)
    {
        cout << "this: " << this << ", text: " << text << endl;
    }
};

struct SampleFunctor: public unary_function<SampleClass*,void>
{
    char* text;
    SampleFunctor(char* text_): text(text_) {}

    result_type operator() (argument_type argu)
    {
        argu->execute(text);
    }
};

void main()
{
    map<int,SampleClass*> mymap;
    SampleClass s1, s2;
    mymap[0] = &s1;
    mymap[1] = &s2;

    SampleFunctor myfunctor("my text");

    for_each(mymap.begin(), mymap.end(), passSecond<int>(myfunctor));
}

Ответы [ 3 ]

1 голос
/ 27 января 2009

Вы можете получить все значения, используя std::transform:

std::multimap<Module, Patch *> patches;

std::vector<Patch *> values(patches.size());
std::transform(patches.begin(), patches.end(), values.begin(),
    std::select2nd<std::multimap<Module, Patch *>::value_type>());

Тогда std::for_each на этом векторе:

std::for_each(values.begin(), values.end(), ApplyOnCondition(...));
1 голос
/ 27 января 2009

У меня есть стартовое решение:

for_each(patches.begin(), patches.end(), 
         boost::bind(ApplyOnCondition(...),
                     boost::bind(&map_type::value_type::second, _1)));

Это берет :: second член пары и толкает его вперед к оператору ApplyOnCondition (). map_type - это тип карты (multimap<Module, Patch*> конечно).

0 голосов
/ 21 августа 2014

Вот решение, которое больше похоже на то, как вы бы это делали на функциональном языке:

  /**
   * Applies a function to the values in a map, producing a map with the same
   * keys but containing the values produced by the function.
   * In Haskell, the type of this function would be something like
   * mapply :: (a -> b) -> Map c a -> Map c b
   */
  template<typename Function, typename Key, typename InValue, typename OutValue>
  static std::map<Key, OutValue> mapply(Function function, const std::map<Key, InValue>& inMap) {
    typedef typename std::map<Key, InValue>::const_iterator InIterator;
    const InIterator end(inMap.end());
    std::map<Key, OutValue> outMap;
    for (InIterator i(inMap.begin()); i != end; ++i) {
      outMap.insert(std::make_pair(i->first, function(i->second)));
    }
    return outMap;
  }

Этот код соответствует C ++ 98. В C ++ 11 это можно упростить.

Также обратите внимание, что функция возвращает карту по значению. Это нормально, если у вас нет признаков того, что это является узким местом для вашей производительности (преждевременная оптимизация - корень всех зол), но если у вас огромная карта и вы не доверяете компилятору оптимизацию, вы, вероятно, предпочли бы решение, в котором функция «mapply» получает параметр out по ссылке: карту, которая заполняется парами ключ-значение.

...