2011-09-28 19 views
100

Para una clase, deseo almacenar algunos punteros a funciones miembro de la misma clase en uno map almacenando objetos std::function. Pero no llego justo al inicio con este código:Uso de objetos std :: function genéricos con funciones miembro en una clase

class Foo { 
    public: 
     void doSomething() {} 
     void bindFunction() { 
      // ERROR 
      std::function<void(void)> f = &Foo::doSomething; 
     } 
}; 

recibo error C2064: term does not evaluate to a function taking 0 arguments en xxcallobj combinado con algunos errores de instancias de plantilla extraños. Actualmente estoy trabajando en Windows 8 con Visual Studio 2010/2011 y en Win 7 con VS10 también falla. El error debe basarse en algunas reglas raras de C++ que no sigo.

EDITAR: Yo hago NO use boost. Esto es C++ 11 integrado en el compilador de MS.

+0

La respuesta correcta es usar 'std :: invoke', que no es compatible con MSVC2013. Esta pregunta está un poco pasada de moda. – Mikhail

Respuesta

180

una función miembro no estática debe ser llamado con un objeto. Es decir, siempre implícitamente pasa "este" puntero como argumento.

Debido a su firma std::function especifica que su función no toma ningún argumento (<void(void)>), debe se unen la primera (y el único) argumento.

std::function<void(void)> f = std::bind(&Foo::doSomething, this); 

Si desea enlazar una función con parámetros, es necesario que especifique los marcadores de posición:

using namespace std::placeholders; 
std::function<void(int,int)> f = std::bind(&Foo::doSomethingArgs, this, _1, _2); 

O, si su compilador soporta C++ 11 lambdas:

std::function<void(int,int)> f = [=](int a, int b) { 
    this->doSomethingArgs(a, b); 
} 

(No tengo un compilador capaz de C++ 11 en la mano en este momento, así que no puedo verificar este.)

+1

Como no dependo del impulso, usaré expresiones lambda;) ¡Sin embargo, gracias! –

+3

@AlexB: Boost.Bind no usa ADL para los marcadores de posición, los coloca en un espacio de nombre anónimo. – ildjarn

+11

Recomiendo evitar la captura global [=] y usar [esto] para que quede más claro lo que se captura (Scott Meyers - Effective Modern C++ Capítulo 6. elemento 31 - Evitar modos de captura predeterminados) –

53

O se necesita

std::function<void(Foo*)> f = &Foo::doSomething; 

modo que se le puede llamar en cualquier instancia, o que necesitan de obligar a una instancia específica, por ejemplo this

std::function<void(void)> f = std::bind(&Foo::doSomething, this); 
3

Si necesita almacenar una función miembro sin la instancia de clase, puede hacer algo como esto:

class MyClass 
{ 
public: 
    void MemberFunc(int value) 
    { 
     //do something 
    } 
}; 

// Store member function binding 
auto callable = std::mem_fn(&MyClass::MemberFunc); 

// Call with late supplied 'this' 
MyClass myInst; 
callable(&myInst, 123); 

¿cuál sería el tipo de almacenamiento de aspecto similar sin automático? Algo como esto:

std::_Mem_fn_wrap<void,void (__cdecl TestA::*)(int),TestA,int> callable 

También se puede pasar este almacenamiento función a una función estándar de unión

std::function<void(int)> binding = std::bind(callable, &testA, std::placeholders::_1); 
binding(123); // Call 

pasado y futuras notas: Una interfaz anterior std :: mem_func existía, pero desde entonces ha desaprobado. Existe una propuesta, publicar C++ 17, para hacer pointer to member functions callable. Esto sería muy bienvenido.

+0

@Danh 'std :: mem_fn' **no quitar; un montón de sobrecargas innecesarias. Por otro lado, 'std :: mem_fun' quedó en desuso con C++ 11 y se eliminará con C++ 17. –

+0

@Danh Eso es exactamente de lo que estoy hablando;) La primera sobrecarga "básica" sigue ahí: 'plantilla mem_fn no especificado (RT :: *);', y no funcionará lejos. –

+0

@Danh Lea [DR] (https://cplusplus.github.io/LWG/lwg-defects.html#2048) detenidamente. 12 de las 13 sobrecargas fueron eliminadas por el DR. Ese último * no era * (y no lo será, ni en C++ 11 ni en C++ 14). –

Cuestiones relacionadas