2010-07-08 26 views
31

Tengo un método como esteEscribir una función que acepta una expresión lambda como argumento

template<typename T, typename U> 
map<T,U> mapMapValues(map<T,U> old, T (f)(T,U)) 
{ 
    map<T,U> new; 
    for(auto it = old.begin(); it != old.end(); ++it) 
    { 
     new[it->first] = f(it->first,it->second); 
    } 
    return new; 
} 

y la idea es que usted lo llamaría como esto

BOOST_AUTO_TEST_CASE(MapMapValues_basic) 
{ 
    map<int,int> test; 
    test[1] = 1; 
    map<int,int> transformedMap = VlcFunctional::mapMapValues(test, 
     [&](int key, int value) -> int 
     { 
      return key + 1; 
     } 
    ); 
} 

Sin embargo me sale el error : ninguna instancia de la plantilla de función "VlcFunctional :: mapMapValues" coincide con los tipos de argumentos de la lista de argumentos son: (std :: map, std :: allocator >>, __lambda1)

¿Alguna idea de lo que estoy haciendo mal? Visual Studio 2008 y el compilador Intel C++ 11.1

+0

'new' es una palabra clave que no puede haber una variable llamada' new' – Motti

+1

jaja. Sí, bien visto, porque el código no encontró los argumentos correctos de la plantilla, esto en realidad nunca se compiló, así que el compilador nunca se molestó en señalarme :) –

+0

No estoy seguro de que se necesite "-> int" para la función lambda – serup

Respuesta

34

Su función espera un puntero de función, no un lambda.

En C++, hay, en general, 3 tipos de "objetos invocables".

  1. Punteros de función.
  2. Objetos de función.
  3. Funciones Lambda.

Si usted quiere ser capaz de utilizar todos estos en su interfaz de la función, entonces se podría utilizar std::function:

template<typename T, typename U> 
map<T,U> mapMapValues(map<T,U> old, std::function<T(T, U)> f) 
{ 
    ... 
} 

Esto permitirá que la función a ser llamada utilizando cualquiera de los tres tipos de objetos invocables arriba. Sin embargo, el precio de esta conveniencia es una pequeña cantidad de sobrecarga en invociones en la función (generalmente una verificación de puntero nulo, luego una llamada a través de un puntero de función). Esto significa que la función casi con certeza no está en línea (excepto quizás con WPO/LTO avanzado).

Como alternativa, puede agregar un parámetro de plantilla adicional para tomar un tipo arbitrario para el segundo parámetro. Esto será más eficiente, pero perderá seguridad de tipo en la función utilizada, y podría provocar una mayor hinchazón del código.

template<typename T, typename U, typename F> 
map<T,U> mapMapValues(map<T,U> old, F f) 
+0

Me tropecé con el segundo enfoque mientras examinaba la fuente std :: transform, sin embargo, todavía no puedo obtener el primer enfoque para el trabajo debido a un error: namespace "std" no tiene "función" de miembro. ¿Hay un incluido que necesito? –

+0

Relacionado con el segundo enfoque: hay utilidades de 'Boost' para extraer los tipos de retorno y la lista de parámetros de una función, me pregunto si funcionarían con un objeto lambda o función y podrían usarse allí con asertos estáticos dentro del código. –

+0

@matthieu, a qué utilidades también te refieres. Creía que la función boost :: debería lograr principalmente el mismo efecto que @peter sugerido con std :: function pero tenía los mismos problemas que utilizar un puntero a función en la declaración. –

12

declaración de su tipo de parámetro T (f)(T,U) es de tipo 'función libre de tomar una T y una U y devolviendo una T'. No puede pasarle una lambda, un objeto de función ni nada que no sea una función real con esa firma.

Se podría resolver esto cambiando el tipo del parámetro a std::function<T(T,U)> así:

template<typename T, typename U> 
map<T,U> mapMapValues(map<T,U> old, std::function<T(T,U)>) 
{ 
} 

Como alternativa, podría declarar el tipo de función como un argumento de plantilla de la siguiente manera:

template<typename T, typename U, typename Fn> 
map<T,U> mapMapValues(map<T,U> old, Fn fn) 
{ 
    fn(...); 
} 
+0

Gracias Joe, marqué la respuesta de Peter solo porque fue un minuto más rápido que tú. Gracias también. –

5

expresiones lambda con la lista de captura de vacío deben decaer a funcionar punteros, según n3052. Sin embargo, parece que esta característica no está implementada en VC++ y solo parcialmente en g ++, consulte mi SO question.

+0

y nada en Intel C++ 11.1 –

10

Me gustaría contribuir con este ejemplo simple pero que se explica por sí mismo. Muestra cómo pasar "cosas invocables" (funciones, objetos de función y lambdas) a una función o a un objeto.

// g++ -std=c++11 thisFile.cpp 

#include <iostream> 
#include <thread> 

using namespace std; 

// ----------------------------------------------------------------- 
class Box { 
public: 
    function<void(string)> theFunction; 
    bool funValid; 

    Box() : funValid (false) { } 

    void setFun (function<void(string)> f) { 
    theFunction = f; 
    funValid = true; 
    } 

    void callIt() { 
    if (! funValid) return; 
    theFunction (" hello from Box "); 
    } 
}; // class 

// ----------------------------------------------------------------- 
class FunClass { 
public: 
    string msg; 
    FunClass (string m) : msg (m) { } 
    void operator() (string s) { 
    cout << msg << s << endl; 
    } 
}; 

// ----------------------------------------------------------------- 
void f (string s) { 
    cout << s << endl; 
} //() 

// ----------------------------------------------------------------- 
void call_it (void (*pf) (string)) { 
    pf("call_it: hello"); 
} //() 

// ----------------------------------------------------------------- 
void call_it1 (function<void(string)> pf) { 
    pf("call_it1: hello"); 
} //() 

// ----------------------------------------------------------------- 
int main() { 

    int a = 1234; 

    FunClass fc (" christmas "); 

    f("hello"); 

    call_it (f); 

    call_it1 (f); 

    // conversion ERROR: call_it ([&] (string s) -> void { cout << s << a << endl; }); 

    call_it1 ([&] (string s) -> void { cout << s << a << endl; }); 

    Box ca; 

    ca.callIt(); 

    ca.setFun (f); 

    ca.callIt(); 

    ca.setFun ([&] (string s) -> void { cout << s << a << endl; }); 

    ca.callIt(); 

    ca.setFun (fc); 

    ca.callIt(); 

} //() 
+1

¡Grandes ejemplos, gracias! :) –

+0

Los tipos de hormigón sin plantilla eran exactamente lo que necesitaba ver –

0

Aquí es algún ejemplo de cómo pasar una función como parámetro

class YourClass 
{ 
void YourClass::callback(void(*fptr)(int p1, int p2)) 
{ 
    if(fptr != NULL) 
     fptr(p1, p2); 
} 
}; 

void dummyfunction(int p1, int p2) 
{ 
    cout << "inside dummyfunction " << endl; 
} 

YourClass yc; 

// using a dummyfunction as callback 
yc.callback(&dummyfunction); 

// using a lambda as callback 
yc.callback([&](int p1, int p2) { cout << "inside lambda callback function" << endl; }); 

// using a static member function 
yc.callback(&aClass::memberfunction); 
Cuestiones relacionadas