2009-01-27 6 views
1

J'ai du mal à utiliser std :: for_each et d'autres algorithmes avec un multimap, et je veux savoir si quelqu'un pourrait m'aider à développer un foncteur capable de passer le paramètre approprié aux fonctions "génériques". Mon problème spécifique avec map/multimap est que leurs itérateurs évaluent une paire std :: à la place de la valeur contenue (je veux dire, le type mapped) avec laquelle je dois travailler. Donc, ma question est, est-il un moyen de passer la valeur appropriée à une fonction conçue pour fonctionner avec l'un des types de carte/multimap contenus?Comment "adapter" les foncteurs à utiliser avec map/multimap?

Voici mon exemple de code:

// 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(...)); 

Je pense que peut-être bind1st ou bind2nd avec conjonction avec mem_fun pourrait résoudre ce problème, ou l'autre manière que je peux penser est de créer une nouvelle foncteur qui stocke le foncteur original et passe le bon membre de la paire, mais je ne parviens pas à obtenir un bon résultat. Quelqu'un d'expérimenté en STL peut-il donner quelques conseils?

EDIT 1 

Ok, le meilleur que je peux obtenir, sans utiliser boost ou un conteneur temporaire supplémentaire est la suivante:

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

Répondre

1

J'ai une solution de coup de pouce dans le début:

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

Cela prend le second membre de la paire, et le pousse vers l'opérateur de ApplyOnCondition(). map_type est le type de la carte (multimap<Module, Patch*> bien sûr).

1

Vous pouvez saisir toutes les valeurs à l'aide 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>()); 

Puis std::for_each sur ce vecteur:

std::for_each(values.begin(), values.end(), ApplyOnCondition(...)); 
0

est ici une solution qui fonctionne plus comme la façon dont vous le faire dans un langage fonctionnel:

/** 
    * 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; 
    } 

Ce code est conforme à la norme C++ 98 . En C++ 11, il pourrait être simplifié.

Notez également que la fonction renvoie la carte par valeur. C'est correct si vous n'avez aucune indication que cela soit un goulot d'étranglement pour vos performances (l'optimisation prématurée est la racine de tous les maux), mais si vous avez une énorme carte et ne faites pas confiance à votre compilateur, une solution où la fonction "mapply" prend un paramètre out par référence: une carte à remplir avec des paires clé-valeur.

Questions connexes