2010-02-22 10 views
8

Tengo un mapa que almacena una estructura simple con una clave. La estructura tiene dos funciones miembro, una es const y la otra no. Logré llamar a la función const usando std :: for_each sin ningún problema, pero tengo algunos problemas para llamar a la función no const.Boost.Bind para acceder a los elementos de std :: map en 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))); 

La llamada a la función miembro const funciona bien, pero parece impulsar internamente espera un MyStruct const en alguna parte, y por lo tanto se produce el siguiente error de compilación en MSVC7.1.

impulso \ bind \ mem_fn_template.hpp (151): error C2440: 'argumento': no ​​se puede convertir de 'MyStruct const * __ W64' a 'MyStruct * const'

lo agradecería cualquier ayuda sobre cómo establecer los parámetros de la plantilla correctamente, de modo que bind reconoce los parámetros correctamente y me permite llamar a la función non const.

gracias, Carl

+0

¿Qué tal si una copia de seguridad y nos dice lo que está realmente tratando de lograr aquí?Usar for_each con un mapa con boost :: bind * might * puede ser razonable, pero las posibilidades son bastante buenas de que un enfoque general diferente funcione mejor (muchas veces surge este tipo de pregunta, es porque 'std :: for_each' es una mala elección para la situación, y algo como 'std :: copy' o std :: accumulate' harían el trabajo mucho más simple). –

+0

El MyStruct se usa en una especie de sistema de partículas, donde MyStruct es la partícula. La función const es una función draw(), la función non-const calcula la nueva posición. La clave en el mapa es la fecha de creación. De todos modos, en el momento en que publiqué la pregunta, se trataba más sobre cómo hacer ese trabajo que si este fuera un buen diseño al principio. – Carl

Respuesta

8

IIRC, Boost.Bind usa boost::mem_fn por su capacidad de vincular a los miembros. Ahora, si mira mem_fun (desplácese hacia abajo a la parte // data member support), verá que typedefs su result_type como const &, mientras que todavía tiene sobrecargas del operador de llamada de función que admite la extracción de un miembro no-const de un argumento no const.

Parece que el problema es que esto confunde el mecanismo de deducción del tipo de devolución de Boost.Bind. Una solución sería así dice explícitamente Enlazar que el resultado no es 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 Buen trabajo de detective. :-) Extrañamente, el uso de 'boost :: lamba :: bind' se compilará sin especificar explícitamente el tipo de devolución. Tal vez 'boost :: lamda :: bind' es más inteligente que' boost :: bind' en la deducción de los tipos de devolución? –

+0

Wow, muchas gracias. Eso compila muy bien. Aunque me encanta usar Boost, todavía es bastante difícil para mí leer la mayor parte de su código, así que fallé. Gracias por tu ayuda. – Carl

+0

Creo que te refieres a 'boost :: mem_fn' – Manuel

0

Un problema que vi: el segundo se unen se llama a un miembro sin función. segundo es un miembro de datos, no es un método de std :: par

+3

Encontré esta técnica en este artículo: http://www.informit.com/articles/article.aspx?p=412354&seqNum=4 Afirma "Puede vincularse a una variable miembro de la misma manera que lo puede hacer con una función miembro , o una función gratuita ". Como el código for_each es esencialmente el mismo para ambas funciones miembro y el problema solo se encuentra en la llamada a la función miembro no consistente, creo que el artículo es correcto. – Carl

4

Si ya eres depende de Boost, puede estar dispuesto para comprobar Boost Foreach

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

Mucho más fácil de leer, aunque no sé sobre problemas de rendimiento.

También tenga en cuenta que no pueda usar con plantilla escrito dentro de la macro sin "escapar" del carácter ,:

  • ya sea por un typedef antes
  • o mediante el uso de un segundo par de paréntesis alrededor del Tipo
+0

Sé Boost Foreach y eso funciona, por supuesto. Pero tengo curiosidad por encontrar la sintaxis correcta para la solución anterior, ya que el código anterior funciona bien para la función const y falla para la función no const. – Carl

+4

Creo que el código correcto sería (elimine la const si desea cambiar los valores): BOOST_FOREACH (MyMap :: value_type const & val, theMap) {...} – Bklyn

+0

y no tiene que enlazar para impulsar cuando utilizo Boost.Bind o Boost.Foreach –

7

Si usted se encuentra el tener que hacer esto mucho le recomiendo que utilice la biblioteca 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)); 
} 

Ha sido aceptado en Boost pero aún no viene con la distribución oficial. Hasta que lo haga, puede download it del Boost Vault (enlace de descarga al archivo comprimido).

+0

Parece mucho más legible y comprensible que la solución de enlace. Definitivamente vale la pena mirar más de cerca. Gracias por el consejo. – Carl

+0

@Carl - también observe que boost :: mem_fn es más fácil de usar que boost :: bind en este caso – Manuel

+0

Esta respuesta es ortogonal a la pregunta, reemplaza el bucle 'std :: for_each' por' boost :: for_each', pero no dice cómo usar 'boost :: bind' como argumento para cualquiera. Aún así, proporciona una solución alternativa. –

Cuestiones relacionadas