2010-02-22 23 views
8

J'ai une carte qui stocke une structure simple avec une clé. La structure a deux fonctions membres, l'une est const l'autre non. J'ai réussi à appeler la fonction const en utilisant std :: for_each sans aucun problème, mais j'ai quelques problèmes pour appeler la fonction non-const.Boost.Bind pour accéder aux éléments std :: map dans std :: for_each

struct MyStruct { 
    void someConstFunction() const; 
    void someFunction(); 
}; 

typedef std::map<int, MyStruct> MyMap; 
MyMap theMap; 

//call the const member function 
std::for_each(theMap.begin(), theMap.end(), 
    boost::bind(&MyStruct::someConstFunction, boost::bind(&MyMap::value_type::second, _1))); 

//call the non-const member function 
std::for_each(theMap.begin(), theMap.end(), 
    boost::bind(&MyStruct::someFunction, boost::bind(&MyMap::value_type::second, _1))); 

L'appel à la fonction membre const fonctionne très bien, mais il semble stimuler quelque part à l'intérieur d'un attend const MyStruct, et ne satisfait donc pas l'erreur de compilation suivante dans MSVC7.1.

boost \ bind \ mem_fn_template.hpp (151): erreur C2440: 'argument': ne peut pas convertir 'const MyStruct * __ w64' à 'MyStruct * const'

J'apprécierais tout Aide sur la façon de définir correctement les paramètres du modèle, donc bind reconnaît les paramètres correctement et laisse-moi appeler la fonction non const.

grâce, Carl

+0

Que diriez-vous si vous sauvegardez et dites-nous ce que vous essayez vraiment d'accomplir ici?Utiliser for_each avec une carte avec boost :: bind * pourrait être raisonnable, mais il y a de fortes chances qu'une approche générale différente fonctionne mieux (plusieurs fois ce genre de question se pose, parce que 'std :: for_each' est un mauvais choix pour la situation, et quelque chose comme 'std :: copy' ou std :: accumulate' ferait le travail beaucoup plus simplement). –

+0

Le MyStruct est utilisé dans une sorte de système de particules, où MyStruct est la particule. La fonction const est une fonction draw(), la fonction non-const calcule la nouvelle position. La clé de la carte est la date de création. De toute façon, au moment où j'ai posté la question, c'était plus sur la façon de faire ce travail que si c'était un bon design au début. – Carl

Répondre

8

IIRC, Boost.Bind utilise boost::mem_fn pour sa capacité de liaison aux membres. Maintenant, si vous regardez mem_fun (faites défiler jusqu'à la partie // data member support), vous verrez qu'il typedefs son type result_type comme const &, alors qu'il a encore des surcharges de l'opérateur d'appel de fonction supportant l'extraction d'un membre non-const de un argument non-const.

Il semble donc que le problème est que cela confond le mécanisme de déduction du type de retour de Boost.Bind. Une solution serait donc de dire explicitement à Bind que le résultat n'est pas const:

//call the non-const member function 
std::for_each(theMap.begin(), theMap.end(), 
    boost::bind(&MyStruct::someFunction, 
     boost::bind<MyStruct&>(&MyMap::value_type::second, _1) 
    ) 
); 
+0

+1 Beau travail de détective. :-) Bizarrement, utiliser 'boost :: lamba :: bind' compilera sans spécifier explicitement le type de retour. Peut-être que boost :: lamda :: bind' est plus intelligent que 'boost :: bind' en déduisant les types de retour? –

+0

Wow, merci beaucoup. Cela compile bien. Bien que j'adore utiliser Boost, il m'est toujours difficile de lire la plupart de leur code, alors j'ai échoué. Merci de votre aide. – Carl

+0

Je pense que vous voulez dire 'boost :: mem_fn' – Manuel

0

Un problème que je repéré: la deuxième liaison est appelé à un membre non fonctionnel. second est un membre de données, pas une méthode de std :: paire

+3

J'ai trouvé cette technique dans cet article: http://www.informit.com/articles/article.aspx?p=412354&seqNum=4 Il indique "Vous pouvez lier à une variable membre comme vous pouvez avec une fonction membre ou une fonction libre. ". Comme le code for_each est essentiellement le même pour les deux fonctions membres et que le problème n'est rencontré que dans l'appel à la fonction membre non-const, je suppose que l'article est correct. – Carl

4

Si vous êtes déjà dépendez Boost, vous pouvez être prêt à vérifier Boost Foreach

BOOST_FOREACH(MyMap::value_type const& val, MyMap) 
{ 
    val.second.someConstFunction(); 
} 

Beaucoup plus facile à lire, même si je ne sais pas sur les problèmes de performance.

Notez également que vous ne pouvez utiliser dans le typé basé sur un modèle macro sans « échapper » le caractère ,:

  • soit par un typedef avant
  • ou en utilisant une deuxième paire de parenthèses autour du type
+0

Je connais Boost Foreach et ça marche bien sûr. Mais je suis juste curieux de trouver la syntaxe correcte pour la solution ci-dessus, puisque le code ci-dessus fonctionne bien pour la fonction const et échoue pour le non-const. – Carl

+4

Je crois que le code correct serait (supprimer le const si vous voulez changer les valeurs): BOOST_FOREACH (MyMap :: valeur_type const & val, theMap) {...} – Bklyn

+0

et vous ne devez pas lier à la poussée lors de l'utilisation de Boost.Bind ou Boost.Foreach –

7

Si vous vous trouvez avoir à faire beaucoup, je vous recommande d'utiliser la bibliothèque Boost.RangeEx:

#include <boost/range/algorithm/for_each.hpp> 
#include <boost/range/adaptor/map.hpp> 
#include <boost/mem_fn.hpp> 
#include <map> 

struct MyStruct { 
    void someConstFunction() const; 
    void someFunction(); 
}; 

typedef std::map<int, MyStruct> MyMap; 
MyMap theMap; 

int main() 
{ 
    //call the const member function 
    boost::for_each(theMap | boost::adaptors::map_values, 
        boost::mem_fn(&MyStruct::someConstFunction)); 

    //call the non-const member function 
    boost::for_each(theMap | boost::adaptors::map_values, 
        boost::mem_fn(&MyStruct::someFunction)); 
} 

Il a été accepté dans Boost mais il n'a pas encore été distribué officiellement. Jusqu'à ce que vous le pouvez download it à partir du Boost Vault (lien de téléchargement au fichier zip).

+0

Cela semble beaucoup plus lisible et compréhensible que la solution de liaison. Cela vaut vraiment la peine d'y regarder de plus près. Merci pour le conseil. – Carl

+0

@Carl - remarquez aussi que boost :: mem_fn est plus facile à utiliser que boost :: bind dans ce cas – Manuel

+0

Cette réponse est orthogonale à la question, elle remplace la boucle 'std :: for_each' par' boost :: for_each', mais ne dit pas comment utiliser 'boost :: bind' comme argument. Même ainsi, il fournit une solution de contournement. –